> ## 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.

# Управление тарифами

> Создание, настройка и управление тарифными планами в режиме тарифов

## Обзор

Тарифы — готовые пакеты подписок с фиксированными параметрами. Используются при `SALES_MODE=tariffs` (по умолчанию). В отличие от режима конструктора, пользователь выбирает тариф целиком, а не собирает параметры по шагам.

<Note>
  Управление тарифами доступно из **админ-панели бота** в Telegram и через **Cabinet Admin API**. При первом запуске бот автоматически создает тариф "Стандартный" с ценами из `.env`.
</Note>

## Создание тарифа

### Через админ-панель бота

Мастер создания (6 шагов):

**Шаг 1: Название**

* 2-50 символов
* Примеры: "Базовый", "Премиум", "Бизнес"

**Шаг 2: Трафик**

* Целое число в ГБ
* `0` = безлимитный трафик

**Шаг 3: Устройства**

* Количество устройств (минимум 1)

**Шаг 4: Уровень тарифа (tier)**

* Число от 1 до 10
* Влияет только на визуальное отображение (1 = базовый)

**Шаг 5: Тип тарифа**

* **Периодический** — оплата за период (30/90/180/360 дней)
* **Суточный** — ежедневное списание

**Шаг 6: Цены**

* Для периодического: формат `дни:цена_в_копейках` через запятую
  ```
  30:9900, 90:24900, 180:44900, 360:79900
  ```
* Для суточного: цена в рублях за день (например, `50` или `99.90`)

После создания тариф сразу активен и доступен пользователям.

### Через Cabinet Admin API

```
POST /cabinet/admin/tariffs
```

Принимает JSON со всеми полями тарифа.

## Поля тарифа

### Основные

| Поле            | Тип    | Описание                                    |
| --------------- | ------ | ------------------------------------------- |
| `name`          | строка | Название (2-50 символов)                    |
| `description`   | текст  | Описание (отображается пользователю)        |
| `display_order` | число  | Порядок сортировки (меньше = выше в списке) |
| `is_active`     | bool   | Активен ли тариф                            |
| `tier_level`    | 1-10   | Визуальный уровень                          |

### Параметры подписки

| Поле                  | Тип        | Описание                                          |
| --------------------- | ---------- | ------------------------------------------------- |
| `traffic_limit_gb`    | число      | Лимит трафика (0 = безлимит)                      |
| `device_limit`        | число      | Количество устройств                              |
| `device_price_kopeks` | число/null | Цена доп. устройства. `null` = покупка недоступна |
| `max_device_limit`    | число/null | Максимум устройств. `null` = без ограничения      |

### Серверы

| Поле             | Тип         | Описание                                      |
| ---------------- | ----------- | --------------------------------------------- |
| `allowed_squads` | JSON список | UUID доступных серверов. Пустой = все серверы |

### Ценообразование

| Поле            | Тип         | Описание                                                    |
| --------------- | ----------- | ----------------------------------------------------------- |
| `period_prices` | JSON объект | Цены за периоды: `{"30": 9900, "90": 24900}` (дни: копейки) |

### Суточный тариф

| Поле                 | Тип   | Описание                                      |
| -------------------- | ----- | --------------------------------------------- |
| `is_daily`           | bool  | Суточный режим (устанавливается при создании) |
| `daily_price_kopeks` | число | Цена за сутки в копейках                      |

<Warning>
  Тип тарифа (периодический/суточный) выбирается **при создании** и не может быть изменен после.
</Warning>

### Пользовательский выбор дней

| Поле                   | Тип   | Описание                             |
| ---------------------- | ----- | ------------------------------------ |
| `custom_days_enabled`  | bool  | Разрешить выбор произвольного срока  |
| `price_per_day_kopeks` | число | Цена за день                         |
| `min_days`             | число | Минимальный срок (по умолчанию 1)    |
| `max_days`             | число | Максимальный срок (по умолчанию 365) |

При включении пользователь видит кнопки +/- для выбора срока вместо фиксированных периодов. Цена = `price_per_day_kopeks * количество_дней`.

### Пользовательский выбор трафика

| Поле                          | Тип   | Описание                        |
| ----------------------------- | ----- | ------------------------------- |
| `custom_traffic_enabled`      | bool  | Разрешить выбор объема трафика  |
| `traffic_price_per_gb_kopeks` | число | Цена за 1 ГБ                    |
| `min_traffic_gb`              | число | Минимум ГБ (по умолчанию 1)     |
| `max_traffic_gb`              | число | Максимум ГБ (по умолчанию 1000) |

Стоимость трафика прибавляется к стоимости периода:

```
итого = цена_периода + (цена_за_гб * количество_гб)
```

### Докупка трафика

| Поле                     | Тип         | Описание                                        |
| ------------------------ | ----------- | ----------------------------------------------- |
| `traffic_topup_enabled`  | bool        | Разрешить докупку трафика на этом тарифе        |
| `traffic_topup_packages` | JSON объект | Пакеты: `{"5": 5000, "10": 9000}` (ГБ: копейки) |
| `max_topup_traffic_gb`   | число       | Максимум докупленного трафика (0 = без лимита)  |

### Сброс трафика

| Поле                 | Тип         | Описание                                                             |
| -------------------- | ----------- | -------------------------------------------------------------------- |
| `traffic_reset_mode` | строка/null | `DAY`, `WEEK`, `MONTH`, `NO_RESET` или `null` (глобальная настройка) |

### Триал

| Поле                 | Тип  | Описание                            |
| -------------------- | ---- | ----------------------------------- |
| `is_trial_available` | bool | Использовать для триальных подписок |

<Note>
  Только **один** тариф может быть триальным. При назначении тарифа триальным флаг автоматически снимается со всех остальных.
</Note>

## Ограничение по промо-группам

Тарифы можно ограничить для определенных промо-групп:

* Если у тарифа **нет** привязанных промо-групп — он доступен **всем**
* Если у тарифа **есть** привязанные промо-группы — он виден **только** пользователям из этих групп

Настройка через кнопку **Промо-группы** на странице тарифа в админке. Можно включать/выключать отдельные группы.

## Редактирование

На странице тарифа в админке доступны кнопки для редактирования каждого поля:

| Действие                    | Описание                                                |
| --------------------------- | ------------------------------------------------------- |
| Название                    | Изменить название (2-50 символов)                       |
| Описание                    | Изменить или удалить описание                           |
| Трафик                      | Изменить лимит (0 = безлимит)                           |
| Устройства                  | Изменить количество                                     |
| Цены                        | Изменить цены за периоды (только для периодических)     |
| Уровень                     | Изменить tier (1-10)                                    |
| Цена устройства             | Цена доп. устройства (0 = отключить)                    |
| Макс. устройств             | Максимальный лимит (0 = без ограничения)                |
| Докупка трафика             | Включить/выключить, настроить пакеты и лимит            |
| Сброс трафика               | Выбрать режим (глобальный/день/неделя/месяц/без сброса) |
| Серверы                     | Выбрать доступные серверы                               |
| Промо-группы                | Ограничить доступ по промо-группам                      |
| Суточная цена               | Изменить цену за сутки (только для суточных)            |
| Сделать триальным           | Назначить для триальных подписок                        |
| Активировать/Деактивировать | Скрыть или показать тариф                               |
| Удалить                     | Удалить с подтверждением                                |

## Сортировка

Тарифы отображаются в порядке `display_order` (по возрастанию). Для изменения порядка:

* **Cabinet Admin API**: `PUT /cabinet/admin/tariffs/order` с массивом ID в нужном порядке
* **Админ-панель бота**: редактирование `display_order` каждого тарифа

## Удаление

При удалении тарифа:

1. Показывается предупреждение с количеством активных подписок на этом тарифе
2. Требуется подтверждение
3. Подписки **не удаляются** — у них обнуляется привязка к тарифу (`tariff_id = NULL`)

## Отображение пользователю

В списке тарифов пользователь видит:

* Название тарифа
* Трафик и количество устройств
* Минимальную цену (с учетом скидок промо-группы)

Примеры:

```
Базовый — 100ГБ / 3 устройства от 179₽
Премиум — Безлимит / 10 устройств от 499₽
Суточный — 50ГБ / 1 устройство 50₽/день
```

### Скидки промо-групп

Если пользователь состоит в промо-группе со скидками за период, скидки отображаются на кнопках выбора периода:

```
30 дней — 179₽
90 дней — 449₽ -15%
180 дней — 799₽ -25%
```

## Формула цены

### Периодический тариф

```
итого = цена_периода - скидка_промогруппы
```

С пользовательским выбором дней:

```
итого = (цена_за_день * дни) - скидка
```

С пользовательским выбором трафика:

```
итого = цена_периода + (цена_за_гб * гб) - скидка
```

### Суточный тариф

```
ежедневное_списание = daily_price_kopeks
```

Списание происходит автоматически каждый день. При недостатке баланса подписка ставится на паузу (`is_daily_paused`).

## Синхронизация цен

После любого изменения тарифов (создание, редактирование, удаление, переключение активности) глобальный словарь `PERIOD_PRICES` автоматически пересчитывается из БД. Перезапуск бота не требуется.

## Управление сменой тарифа

Пользователь с активной подпиской может перейти на другой тариф. Направление смены контролируется двумя переменными окружения:

| Переменная                        | По умолчанию | Описание                                             |
| --------------------------------- | ------------ | ---------------------------------------------------- |
| `TARIFF_SWITCH_UPGRADE_ENABLED`   | `true`       | Разрешить переход на более дорогой тариф (апгрейд)   |
| `TARIFF_SWITCH_DOWNGRADE_ENABLED` | `true`       | Разрешить переход на более дешёвый тариф (даунгрейд) |

Сравнение цен тарифов происходит по стоимости за 30-дневный период. Возможные комбинации:

| Апгрейд | Даунгрейд | Поведение                                       |
| ------- | --------- | ----------------------------------------------- |
| `true`  | `true`    | Пользователь может переключиться на любой тариф |
| `true`  | `false`   | Только переход на более дорогой тариф           |
| `false` | `true`    | Только переход на более дешёвый тариф           |
| `false` | `false`   | Смена тарифа полностью отключена                |

<Note>
  При смене тарифа оставшиеся дни подписки пересчитываются пропорционально разнице в стоимости.
</Note>

## Связанные настройки

| Переменная                        | Описание                                                            |
| --------------------------------- | ------------------------------------------------------------------- |
| `SALES_MODE`                      | `tariffs` (по умолчанию) или `classic`                              |
| `TRIAL_TARIFF_ID`                 | ID триального тарифа (устаревшее, используйте `is_trial_available`) |
| `TARIFF_SWITCH_UPGRADE_ENABLED`   | Разрешить апгрейд тарифа                                            |
| `TARIFF_SWITCH_DOWNGRADE_ENABLED` | Разрешить даунгрейд тарифа                                          |
