Commit Graph

3 Commits

Author SHA1 Message Date
Дмитрий 2460623ae0 feat(смс-клиент): ночной лид получает авто-СМС утром и ровно одно
Этап 3 «Время и цена», Task 4. Строка листа 3.6, дозакрыта 3.1.

Авто-СМС на новый лид больше не будит людей ночью. Джоб спрашивает про час у того же
SmsQuietHours, что и рассылка, и если у ПОЛУЧАТЕЛЯ окно закрыто — переставляет сам себя
на момент его открытия. В журнал при этом не пишет ничего: идемпотентность у авто-СМС —
по наличию любой записи по этому лиду, и запись «ждём утра» навсегда закрыла бы лиду
дорогу (класс поломки В-49). «Ровно одно» держит прежняя защита: три запуска подряд дают
одно сообщение.

Проверка стоит ПОСЛЕ отбора, а не сразу после идемпотентности, как предлагал план.
Сначала выясняется, уйдёт ли номер вообще (стоп-лист, дубль, нет маршрута), и только
потом — когда именно. Иначе номеру из стоп-листа вместо окончательной причины досталось
бы «ждём региона», и человек ждал бы отправки, которой не будет никогда (В-67).
Контрольный тест это стережёт: перенос проверки вперёд его роняет.

Москву при пустом регионе не подставляем. План велел «нет региона — считаем московским»,
но эту запись владелец отменил (В-85, журнал В-98): вставь я её дословно, камчатский лид,
пришедший в московский вечер, получил бы СМС в четыре утра. Сделка без региона сутки ждёт
уточнения — джоб переставляет себя раз в час и молчит, — потом получает одну честную
запись skipped_unknown_region. Подписи причины заведены в обоих словарях экрана сразу
(ловушка В-70), хотя журнала авто-СМС на портале пока нет вовсе — это существующая дыра,
названа отдельно (В-102).

Старым тестам авто-СМС проставлен регион в фикстурах и зафиксирован час: тот же ремонт,
что в В-93 — боевое правило не ослаблял.

Прогоны: СМС 202/202 (было 195, 7 новых тестов), приём лидов 17/17, фронт 1663 зелёных,
phpstan 0, pint чисто, типы — те же 8 чужих давних. Миграций нет.

Вырезанием проверено четырежды: убрал проверку окна — покраснели три теста; убрал суточный
срок — покраснел тест про честную причину; поставил проверку региона до стоп-листа —
покраснели «дверь открыта» и контрольный про стоп-лист; убрал подпись из словаря журнала —
покраснел фронтовый тест.

Живьём на локальной базе (18:15 в Москве, 03:15 на Камчатке): московский лид ушёл сразу,
камчатский отложен на 22:00 UTC — это десять утра следующего дня у него; свежая сделка без
региона отложена на час, двухдневная получила причину. Затем открыл окно камчатской и
запустил трижды — сообщение одно. Стенд возвращён как был.

🪤 Урок В-101: под тестовой очередью «переставить себя» исполняется немедленно — джоб зовёт
себя без конца и прогон виснет. В таких тестах обязателен Queue::fake().

⚠️ Ветку по-прежнему нельзя выкатывать до Task 5 (В-93).
2026-07-28 18:23:15 +03:00
Дмитрий c47c3fb4d5 fix(смс-клиент): недосписание при повторе, срок для рассылки по сделкам, заслонка баланса + понятный интерфейс
Корректность/деньги:
- джоб рассылки: списание ПОШТУЧНО и атомарно с записью в журнал, ключ идемпотентности на номер, факт из БД — нет недосписания при падении посреди рассылки и повторе
- валидация: срок обязателен при source=deals, иначе выборка по всей истории сделок
- «Отправить» гаснет при нехватке свободного баланса; index отдаёт баланс/заморозку
- помесячный джоб имени: устойчив к пустым настройкам, сигнал в лог при автоотключении за долг
- цена одного авто-СМС + подтверждение при включении авто-рассылки
- загрузка контактов сообщает число нераспознанных номеров

Понятность для клиента:
- «СМС» вместо «сегментов», «пробный режим» вместо «песочницы», человеческие причины пропуска, шаги 1-2-3, выгода своего имени, факт вместо оценки в таблице, имя и текст в подтверждении

Тесты: бэкенд ClientSms 83/83, фронт СМС 50/50, приём лидов 10/10; pint/stan чисто.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 10:27:14 +03:00
Дмитрий 51b8f501ae feat(смс-клиент): бэкенд имени и авто-рассылки
Жизненный цикл имени: клиент requestSender (freeze первого месяца) / disableSender;
админ approve (charge+release, active, paid_until+1мес) / reject / disable. Помесячная
оплата ChargeSmsNameFeeJob (проверка средств ДО списания, долг>29 дней→suspended,
идемпотентно по external_key), расписание в console.php. Авто-рассылка: DealSmsObserver
(защитный, freshness-guard, НИКОГДА не роняет приём лида) → SendAutoSmsForDealJob
(best-effort, идемпотентно по deal_id, песочница/деньги/маршрут). Действующее имя
кампании = active-имя тенанта иначе liderra.ru.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 23:20:52 +03:00