# Приёмка liderra.ru — ПУНКТ №8: Тариф-ступень (цена падает на нужном по счёту лиде)

> **Для исполнителя:** формат PR1 (одобрен 20.06). Каждый шаг = `🔧 Код-факт (file:line)` · `🎬 Действие` · `📋 ОТЧЁТ (было→ожидали→стало + 📸/📝)` · `❌ Если не так`. Техника «под капотом»; владельцу — человеческие карточки.

**Цель:** показать, что чем больше заявок клиент получает за месяц, тем **дешевле** становится следующая заявка — и переход на новую цену происходит **ровно на нужном по счёту лиде**, ни раньше, ни позже. Это видно глазами в ленте списаний: две соседние заявки списываются по разной цене на стыке ступени.

**Что снимаем глазами:** раздел **Биллинг** — лента списаний, где на стыке ступени цена меняется (500 ₽ → 450 ₽). Живые 📸.

**Источник сценария:** свод проверок №8 (M-TIER); R3 «движок».

---

## 🔧 Код-факты (подтверждено чтением 20.06)

- Цена за лид выбирается по **порядковому номеру лида в месяце**: `tier = resolveForCount(tiers, delivered_in_month + 1)` — [LedgerService.php:53-57](../../../app/app/Services/Billing/LedgerService.php#L53-L57). Счётчик месяца растёт на 1 за каждое списание — [:86](../../../app/app/Services/Billing/LedgerService.php#L86). Цена и номер ступени пишутся в `lead_charges` — [:93-95](../../../app/app/Services/Billing/LedgerService.php#L93-L95).
- Логика ступеней — [PricingTierResolver::resolveForCount :25-54](../../../app/app/Services/Billing/PricingTierResolver.php#L25-L54): ступень 1 покрывает лиды `1..сумма1`, ступень 2 — следующие, …, последняя (`leads_in_tier=NULL`) — «всё свыше».
- Дефолтные 7 ступеней — [PricingTierSeeder :18-25](../../../app/database/seeders/PricingTierSeeder.php#L18-L25):

| Ступень | Какие по счёту лиды месяца | Цена за лид |
|---|---|---|
| T1 | 1 … 100 | **500 ₽** |
| T2 | 101 … 300 | **450 ₽** |
| T3 | 301 … 700 | 400 ₽ |
| T4 | 701 … 1500 | 350 ₽ |
| T5 | 1501 … 3000 | 300 ₽ |
| T6 | 3001 … 6000 | 270 ₽ |
| T7 | 6001 и далее | 250 ₽ |

> Числа берутся из **живой** `pricing_tiers`, не зашиты — всегда сверять с базой на прогоне (на проде планируется снижать вплоть до ≤65 ₽).

---

## Идея лёгкого показа (чтобы не вливать 101 заявку)

Граница T1→T2 — между лидом №100 (500 ₽) и №101 (450 ₽). Чтобы показать стык дёшево:
1. выставить выбранному клиенту счётчик месяца `delivered_in_month = 99`;
2. влить **2 заявки** → лид №100 спишется по **500 ₽**, лид №101 — уже по **450 ₽**;
3. в Биллинге видно два соседних списания с разной ценой — это и есть «ступень сработала ровно на 101-м».

(На проде — либо органический переход при реальном объёме, либо тот же приём через админа на тест-клиенте.)

---

## Шаг 8-0 — Подвести клиента к границе ступени

**🔧 Код:** счётчик — `tenants.delivered_in_month`; используется в [LedgerService :53-57](../../../app/app/Services/Billing/LedgerService.php#L53-L57).
**🎬 Действие (лок):** выбрать тест-клиента с достаточным балансом (≥ ~1000 ₽); выставить `UPDATE tenants SET delivered_in_month=99 WHERE id=<C>`. Зафиксировать баланс «до».

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Счётчик месяца клиента | 0/другой | 99 (стоит у границы T1→T2) | _(слот прогона)_ | 📝 SQL `SELECT delivered_in_month,balance_rub FROM tenants WHERE id=<C>` |
| Баланс «до» | — | зафиксирован для сверки | _(слот)_ | 📸 Дашборд/Биллинг (баланс до) |

**❌ Если не так:** счётчик не выставился → ступень покажется не на той заявке.

---

## Шаг 8-1 — Влить лид №100 (последний по 500 ₽)

**🔧 Код:** списание — [LedgerService :53-95](../../../app/app/Services/Billing/LedgerService.php#L53-L95); ступень для ordinal 100 = T1 (500 ₽) по таблице.
**🎬 Действие (лок):** влить 1 заявку этому клиенту (через `imitation:seed`/вебхук).

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Списание за лид №100 | — | **500 ₽** (ещё ступень T1) | _(слот)_ | 📸 Биллинг — новая строка списания 500 ₽ |
| Счётчик месяца | 99 | 100 | _(слот)_ | 📝 SQL `delivered_in_month=100` |

**❌ Если не так:** цена ≠ 500 на 100-м лиде → граница ступени смещена.

---

## Шаг 8-2 — Влить лид №101 (первый по 450 ₽) — ступень сработала

**🔧 Код:** для ordinal 101 resolver отдаёт T2 (450 ₽) — [PricingTierResolver :39-48](../../../app/app/Services/Billing/PricingTierResolver.php#L39-L48).
**🎬 Действие (лок):** влить ещё 1 заявку тому же клиенту.

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Списание за лид №101 | 500 ₽ (как №100) | **450 ₽** (ступень T2) | _(слот)_ | 📸 Биллинг — соседняя строка 450 ₽ (рядом с 500 ₽) |
| Стык ступени | — | переход ровно с 101-го, не раньше | _(слот)_ | 📸 две строки подряд: 500 → 450 |
| Сходимость баланса | — | баланс упал на 500+450 = 950 ₽ | _(слот)_ | 📸 баланс «до» − 950 ₽; 📝 SQL `lead_charges` (tier_no 1 затем 2) |

**💡 Что внутри:** портал считает, сколько заявок клиент уже получил в этом месяце. Пока их ≤100 — цена 500 ₽. Как только пошла 101-я — портал сам, без ручного вмешательства, переводит клиента на следующую, более выгодную цену 450 ₽. Чем больше объём за месяц — тем дешевле каждая заявка. Переход случается точно на нужной по счёту заявке.

**❌ Если не так:** №101 списался по 500 ₽ (ступень не переключилась) / переключился раньше 101-го → дефект тарификации; баланс не сошёлся с 950 ₽ → потеря копеек (стоп, денежный дефект).

---

## Verification №8 (выход из пункта)
- [ ] 📸 Биллинг: соседние списания **500 ₽ → 450 ₽** на стыке (лиды №100 и №101).
- [ ] 📸 Баланс упал ровно на 950 ₽ (500+450).
- [ ] 📝 SQL `lead_charges`: у №100 `tier_no=1, price=50000`; у №101 `tier_no=2, price=45000`.
- [ ] (опц.) повтор на другой границе (например счётчик=300 → №301 уходит на 400 ₽) — доказать, что ступени общие, не одна.

## 📸 vs 📝 в этом пункте
- **📸 Скриншот:** Биллинг — лента списаний с разными ценами на стыке; Дашборд/баланс «до» и «после».
- **📝 Текст:** строки `lead_charges` (tier_no, price_per_lead_kopecks), счётчик `delivered_in_month` — глазами не видны.

## Грабли
- Числа ступеней — из **живой** `pricing_tiers`; перед прогоном сверить (на проде цены ниже дефолтных). Если границы другие — пересчитать, на каком лиде стык.
- Баланс должен быть достаточным, иначе на стыке сработает не ступень, а нехватка (это отдельный пункт №9).
- Счётчик `delivered_in_month` — месячный; приём «выставить 99» допустим только на тест-клиенте.
