# Приёмка liderra.ru — ПУНКТ №4: Лимит и пауза «на лету» (под локом)

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

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

**Что снимаем глазами:** **Проекты** (поставлен на паузу), **Сделки** (паузнутый клиент не получил заявку), счётчик дня (не больше лимита). Живые 📸.

**Источник сценария:** свод проверок №4 (D-LIMIT/D-PAUSE); R3 «движок».

---

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

- **Пауза «на лету» под локом** — перед доставкой проект блокируется (`lockForUpdate`) и **заново** проверяется `is_active`; если клиент нажал «пауза» в окне между отбором и доставкой — заявку **не доставляем** (`paused under lock = stop`), сделка не создаётся, баланс не трогается — [RouteSupplierLeadJob.php:283-301](../../../app/app/Jobs/RouteSupplierLeadJob.php#L283-L301).
- **Лимит под локом** — лимит берётся из зафиксированного слепка, проверяется под блокировкой; пока шла раздача, параллельная доставка могла добить лимит → лишнюю не доставляем — [:280-282](../../../app/app/Jobs/RouteSupplierLeadJob.php#L280-L282), [:303-316](../../../app/app/Jobs/RouteSupplierLeadJob.php#L303-L316).
- Переключатель паузы клиента — `PATCH /api/projects/{id}/toggle-active`: `is_active=false` + `paused_at` + синк паузы к поставщику — [ProjectController.php:264-282](../../../app/app/Http/Controllers/Api/ProjectController.php#L264-L282).
- Фильтр лимита в отборе — `delivered_today < daily_limit` — [LeadRouter.php:145](../../../app/app/Services/LeadRouter.php#L145).

---

## Шаг 4-1 — Поставить проект на паузу

**🔧 Код:** [ProjectController::toggleActive :264-282](../../../app/app/Http/Controllers/Api/ProjectController.php#L264-L282).
**🎬 Действие (лок):** под клиентом поставить его проект на паузу (переключатель в разделе Проекты).

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Проект | активен | **на паузе** (выключен приём) | _(слот прогона)_ | 📸 Проекты (статус «на паузе») |

**❌ Если не так:** пауза не ставится → проверить переключатель.

---

## Шаг 4-2 — Паузнутый клиент НЕ получает заявку (даже текущую)

**🔧 Код:** recheck под локом — [RouteSupplierLeadJob :288-301](../../../app/app/Jobs/RouteSupplierLeadJob.php#L288-L301).
**🎬 Действие (лок):** влить заявку на источник, где этот клиент был бы подходящим; проверить, что ему она не пришла.

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Сделка у паузнутого | — | **не создана** (на паузе = не получает) | _(слот)_ | 📸 Сделки этого клиента (без новой) |
| Баланс паузнутого | X ₽ | **не изменился** | _(слот)_ | 📸 Баланс до = после |
| Заявка у других | — | ушла другим подходящим (поток не сломан) | _(слот)_ | 📸 Сделки другого клиента (есть) |

**💡 Что внутри:** когда клиент ставит проект на паузу, портал перестаёт давать ему заявки **немедленно** — даже ту, что в этот самый момент раздавалась. Перед каждой выдачей он ещё раз сверяется: «проект всё ещё активен?» Если нет — заявку этому клиенту не отдаёт (и денег не списывает), а отправляет другим. Пауза работает мгновенно, без «доедет завтра».

**❌ Если не так:** паузнутый клиент получил заявку / списались деньги → дефект (пауза не мгновенная).

---

## Шаг 4-3 — Снять паузу → снова получает

**🔧 Код:** [toggleActive :264-282](../../../app/app/Http/Controllers/Api/ProjectController.php#L264-L282).
**🎬 Действие (лок):** снять паузу; влить заявку; проверить доставку.

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Проект | на паузе | снова активен | _(слот)_ | 📸 Проекты (активен) |
| Доставка после снятия | — | заявки снова приходят | _(слот)_ | 📸 Сделки (новая) |

**❌ Если не так:** после снятия паузы не получает → дефект возобновления.

---

## Шаг 4-4 — Дневной лимит не превышается под потоком

**🔧 Код:** лимит под локом — [RouteSupplierLeadJob :303-316](../../../app/app/Jobs/RouteSupplierLeadJob.php#L303-L316); фильтр — [LeadRouter :145](../../../app/app/Services/LeadRouter.php#L145).
**🎬 Действие (лок):** клиенту с маленьким лимитом (например 2) влить поток заявок быстрее, чем по одной; проверить, что доставок ровно по лимиту.

**📋 ОТЧЁТ:**
| Что | Было | Ожидалось | Стало (факт) | Чем подтверждаем |
|---|---|---|---|---|
| Доставлено под потоком | 0 | **ровно лимит** (например 2), не больше | _(слот)_ | 📸 Сделки клиента; 📝 `delivered_today = daily_limit` |

**💡 Что внутри:** даже если несколько заявок «прилетают» клиенту почти одновременно, портал не даст превысить его дневной лимит: перед выдачей он под «замком» проверяет, не добит ли лимит уже параллельной заявкой. Лишнюю не отдаёт.

**❌ Если не так:** доставок больше лимита под потоком → дефект (гонка пробивает лимит).

---

## Verification №4 (выход из пункта)
- [ ] 📸 Проект ставится на паузу и снимается.
- [ ] 📸 На паузе клиент не получает заявку (и денег не списывают); заявка уходит другим.
- [ ] 📸 После снятия паузы снова получает.
- [ ] 📸/📝 Под потоком лимит не превышен.

## 📸 vs 📝 в этом пункте
- **📸 Скриншот:** Проекты (пауза/актив), Сделки паузнутого (без новой) и другого (с новой), счётчик дня у лимита.
- **📝 Текст:** проверка `is_active`/лимита под локом, `delivered_today` — частично текстом.

## Грабли
- «Пауза на лету» — тонкий race: проверять, что пауза, нажатая в момент раздачи, успевает сработать (recheck под локом).
- Лимит из слепка (зафиксирован на 18:00/21:00 МСК) — уменьшение лимита после слепка не рвёт уже зафиксированный поток (это by design, не дефект).
- Связка с №9 (автопауза при нехватке) — там пауза ставит сам портал; здесь — клиент вручную.
