> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedolagam.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Промо-система

> Промокоды, промо-группы и персональные предложения

## Обзор

Промо-система включает три связанных механизма:

1. **Промокоды** — одноразовые/многоразовые коды для бонусов
2. **Промо-группы** — тарифные группы со скидками и приоритетами
3. **Персональные промо-предложения** — адресные акции от администратора

## Промокоды

### 5 типов промокодов

| Тип                     | Что дает                                       |
| ----------------------- | ---------------------------------------------- |
| **balance**             | Начисление на баланс (в копейках)              |
| **subscription\_days**  | Продление/создание платной подписки на N дней  |
| **trial\_subscription** | Активация триальной подписки                   |
| **promo\_group**        | Назначение в промо-группу                      |
| **discount**            | Разовая процентная скидка на следующую покупку |

### Создание промокода (админ)

1. Выбор типа (5 вариантов)
2. Ввод кода (3-20 символов, `[A-Za-z0-9_-]`, уникальный)
3. Параметры в зависимости от типа:
   * **Balance**: сумма в рублях (1-10 000)
   * **Days / Trial**: дни (1-3650)
   * **Discount**: процент (1-100)
   * **Group**: выбор из существующих промо-групп
4. Максимум использований (0 = безлимит)
5. Срок действия в днях (0 = бессрочно)
6. Для discount: длительность скидки в часах (0 = до первой покупки)

### Активация пользователем

Пользователь вводит код через кнопку "Промокод" в главном меню.

**Проверки перед активацией:**

1. Формат кода: 3-50 символов, `[A-Za-z0-9_-]`
2. Rate limiting: максимум 5 неудачных попыток за 5 минут
3. Дневной лимит: максимум 5 успешных активаций за 24 часа
4. Код существует, активен, не исчерпан
5. Пользователь еще не использовал этот код
6. Для `first_purchase_only` — пользователь не имел платной подписки

### Эффекты при активации

| Тип                     | Действие                                                                                                |
| ----------------------- | ------------------------------------------------------------------------------------------------------- |
| **balance**             | Начисление на баланс                                                                                    |
| **subscription\_days**  | Если есть подписка — продление. Если нет — создание новой платной подписки с синхронизацией в RemnaWave |
| **trial\_subscription** | Создание триальной подписки (только если нет активной)                                                  |
| **promo\_group**        | Назначение в указанную промо-группу                                                                     |
| **discount**            | Установка `promo_offer_discount_percent` на пользователе. Ошибка, если уже есть активная скидка         |

<Note>
  Если у промокода любого типа указан promo\_group\_id, промо-группа назначается дополнительно при активации.
</Note>

### Управление (админ-панель)

* Список промокодов с пагинацией (10 на страницу)
* Детали: тип, статус, использования, срок
* Редактирование: срок, максимум использований, сумма/дни
* Переключение `first_purchase_only`
* Статистика: общее число, сегодня, история активаций с именами
* Удаление с подтверждением

## Промо-группы

### Что такое промо-группы

Промо-группы — это тарифные уровни, определяющие скидки для пользователей. Каждый пользователь может состоять в нескольких группах, а основная определяется по приоритету (чем выше — тем важнее).

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

| Категория  | Поле                       | Описание                          |
| ---------- | -------------------------- | --------------------------------- |
| Серверы    | `server_discount_percent`  | Скидка на серверы (0-100%)        |
| Трафик     | `traffic_discount_percent` | Скидка на трафик (0-100%)         |
| Устройства | `device_discount_percent`  | Скидка на устройства (0-100%)     |
| Период     | `period_discounts`         | JSON: скидка за длительный период |

Пример period\_discounts: `{60: 10, 90: 20, 180: 40, 360: 70}` — 10% за 60 дней, 70% за год.

### Скидки для базовой группы

Для пользователей без промо-группы (или в группе по умолчанию) доступны базовые скидки за период:

```env theme={null}
BASE_PROMO_GROUP_PERIOD_DISCOUNTS_ENABLED=true
BASE_PROMO_GROUP_PERIOD_DISCOUNTS=60:10,90:20,180:40,360:70
```

### Ограничение серверов

Промо-группы могут ограничивать доступные серверы через связь с server\_squads. Если привязаны серверы — пользователь видит только их.

### Скидки на аддоны

Флаг `apply_discounts_to_addons` (по умолчанию true) определяет, применяются ли скидки группы к дополнительным покупкам (трафик, устройства).

### Автоматическое назначение по тратам

Промо-группы с полем `auto_assign_total_spent_kopeks` автоматически назначаются пользователям при достижении порога трат:

1. После каждого платежа проверяется порог
2. Система находит лучшую подходящую группу (порог меньше или равен тратам пользователя)
3. Группа добавляется (не заменяет другие)
4. Администратор получает уведомление

Пример: Промо-группа "VIP" с порогом 50 000 коп. (500 руб.) — автоматически назначается после траты 500+ рублей.

### Управление (админ-панель)

* **Создание**: название, приоритет, скидки (трафик, серверы, устройства), период-скидки, авто-порог
* **Редактирование**: все поля, переключение скидок на аддоны
* **Просмотр участников** с пагинацией
* **Удаление**: с подтверждением, участники переносятся в группу по умолчанию. Группу по умолчанию удалить нельзя.

## Персональные промо-предложения

### Типы предложений

| Тип                    | Описание                             | Сегменты                                       |
| ---------------------- | ------------------------------------ | ---------------------------------------------- |
| **test\_access**       | Временный доступ к тестовым серверам | Платные активные, триальные активные           |
| **extend\_discount**   | Скидка на продление подписки         | Платные активные                               |
| **purchase\_discount** | Скидка на покупку подписки           | Платные/триальные истекшие, триальные активные |

### Как работают

1. Администратор выбирает шаблон предложения
2. Выбирает сегмент пользователей или конкретного пользователя
3. Система создает индивидуальные записи DiscountOffer с ограниченным сроком действия
4. Пользователям отправляется сообщение с кнопкой CTA
5. Пользователь нажимает кнопку и предложение активируется

### Тестовый доступ к серверам

Предложение test\_access:

1. Указываются серверы (UUID) и длительность (часы)
2. При активации создаются записи SubscriptionTemporaryAccess
3. Серверы добавляются в подписку пользователя и синхронизируются с RemnaWave
4. По истечении срока доступ автоматически убирается фоновой задачей

### Шаблоны предложений

Шаблоны содержат:

* Текст сообщения (с плейсхолдерами: `{discount_percent}`, `{valid_hours}`, `{test_duration_hours}`, `{server_name}`)
* Текст кнопки
* Срок действия предложения (часы)
* Процент скидки или длительность тестового доступа
* Список серверов (для test\_access)

Все шаблоны редактируются из админ-панели.

### Отправка предложений

**По сегменту:**

* Выбор шаблона, выбор сегмента, массовая отправка
* Для test\_access: фильтруются пользователи, уже подключенные к целевому серверу
* Батчевая отправка: 100 за раз, параллелизм до 20

**Индивидуально:**

* Поиск пользователя, детальный профиль, подтверждение, отправка

### Журнал операций

Все действия логируются в PromoOfferLog:

* claimed — пользователь принял предложение
* consumed — предложение использовано
* disabled — предложение деактивировано (вручную или по истечении)

## Настройка

| Переменная                                  | По умолчанию                | Описание                            |
| ------------------------------------------- | --------------------------- | ----------------------------------- |
| `BASE_PROMO_GROUP_PERIOD_DISCOUNTS_ENABLED` | `false`                     | Скидки за период для базовой группы |
| `BASE_PROMO_GROUP_PERIOD_DISCOUNTS`         | `60:10,90:20,180:40,360:70` | Формат: дни и процент через запятую |
