# Промокоды

`/admin/shop/promocodes` — CRUD промокодов для скидок в чекауте.

## Типы скидки

| Type | Поведение | Поле value |
|---|---|---|
| `percent` | Процент от суммы заказа | Значение в процентах (например, 10 = 10%) |
| `fixed` | Фиксированная сумма | Сумма в текущей валюте (например, 500) |
| `free_shipping` | Бесплатная доставка | Не используется (но колонка есть) |

## Поля

| Поле | Что |
|---|---|
| `code` | Уникальный код, который вводит покупатель (UPPERCASE рекомендуется) |
| `type` | См. таблицу выше |
| `value` | Размер скидки |
| `min_amount` | Минимальная сумма заказа для применения (`null` = без ограничений) |
| `usage_limit` | Лимит использований всеми покупателями (`null` = без лимита) |
| `usage_count` | Сколько раз уже применён (инкрементится при каждом успешном применении) |
| `starts_at` | Дата начала действия (`null` = с сейчас) |
| `ends_at` | Дата окончания (`null` = бессрочно) |
| `is_active` | Включён ли |
| `comment` | Внутренняя заметка |

## Применение в чекауте

Сейчас на витрине поле «промокод» вводится вручную в форме `/checkout`. При POST'e сервер:

1. Находит активный код по `code` (регистронезависимо), действующий по датам, не превысивший лимит.
2. Проверяет `min_amount` против `subtotal`.
3. Применяет скидку: `discount = subtotal * value / 100` для percent, `discount = value` для fixed.
4. Сохраняет в заказе как `discount` + `promo_code`.
5. Инкрементит `usage_count`.

Если код невалиден — заказ создаётся без скидки и пользователь видит сообщение.

## Хук для бизнес-логики

Можно расширить условия применения промокода через `shop.promo.apply` (планируется в v1.2) — например, скидка только на определённую категорию.

## База

`shop_promo_codes`: `code` (UNIQUE), `type`, `value`, `min_amount`, `usage_limit`, `usage_count`, `starts_at`, `ends_at`, `is_active`, `comment`.
