# Клиенты

## Откуда берутся

База **ведётся автоматически**. Никаких регистраций не требуется:

- При оформлении заказа на витрине (`/checkout`) `CustomerRepo::upsertFromOrder()` находит клиента по email или создаёт новую запись.
- При ручном создании заказа из админки — тот же путь.
- Если клиент уже существует — обновляются непустые поля (`name`, `phone`, `address`, `city`, `country`, `zip`) с данных свежего заказа.
- `email` — ключ записи (case-insensitive, хранится в нижнем регистре). Изменить email в карточке клиента нельзя.

## Список (`/admin/shop/customers`)

Поиск по имени / email / телефону. Сортировка: по последнему заказу, сумме покупок, числу заказов, имени, дате регистрации.

**Табы**:

| Таб | Условие |
|---|---|
| Все | Все клиенты |
| VIP | `is_vip = true` (ставится вручную в карточке) |
| Повторные | `orders_count > 1` |
| Один заказ | `orders_count = 1` |
| Без заказов | `orders_count = 0` (созданы вручную, без покупок) |

В таблице — аватар-инициалы, имя/email/телефон, число заказов, потрачено, дата последнего заказа, город.

## Карточка клиента (`/admin/shop/customers/{id}`)

- **Статистика** сверху: число заказов, потрачено, средний чек, дата последнего заказа.
- **Профиль** — редактируемая форма: имя, телефон, город, страна, индекс, адрес, заметка о клиенте, флаги VIP и marketing opt-in.
- **История заказов** — все заказы клиента с номером, датой, суммой, статусом. Если у заказа `customer_id` ещё не проставлен (старые заказы до миграции 0007) — fallback-поиск по совпадению email.

## Агрегаты

Поля в `shop_customers`:

| Поле | Что |
|---|---|
| `orders_count` | Кол-во заказов (исключая `cancelled`) |
| `total_spent` | Сумма всех заказов (исключая `cancelled`) |
| `last_order_at` | Таймстамп последнего заказа |

Пересчитываются `CustomerRepo::recalcAggregates()` после:
- успешного создания заказа,
- смены статуса заказа,
- ручного редактирования заказа,
- удаления клиента (отвязывает заказы вместо удаления — историю продаж не теряем).

## Marketing opt-in

Флаг `marketing_opt_in` показывает, что клиент подписался на email-рассылку. По умолчанию `false`. Используется для сегментации (например, для будущего email-плагина).

## Удаление клиента

Заказы клиента **не удаляются**. Они отвязываются (`customer_id = NULL`), но сохраняют snapshot контактов (`customer_email`, `customer_name` и т.д.) — история продаж важнее, чем личные данные.

## API для других плагинов

```php
$repo = new \Celena\Plugin\Shop\Support\CustomerRepo($conn);

// Найти клиента по email (case-insensitive)
$customer = $repo->findByEmail('user@example.com');

// Создать или обновить с данных заказа
$customerId = $repo->upsertFromOrder([
    'email'   => 'user@example.com',
    'name'    => 'Иван Петров',
    'phone'   => '+380...',
    'address' => 'Київ, ...',
    'user_id' => 5,                // если зарегистрированный пользователь
]);

// Пересчитать агрегаты после изменений в заказах
$repo->recalcAggregates($customerId);
```
