Commit Graph

4 Commits

Author SHA1 Message Date
Дмитрий b29a4ca4c4 revert(смс-клиент): отказ получателя убран целиком — решение владельца
Владелец: «нет такой функции и задачи нет, забудь о ней! пришла и пришла смс».
Причина — приписка «Отказ: liderra.ru/s/…» ставила НАШ адрес в рекламное СМС, которое
клиент шлёт своим покупателям: он рекламирует себя, а не нас.

Убрано: страница отказа /s/{token}, таблица коротких ссылок, сервис токенов, приписка
в тексте рассылки, колонка with_optout_link, ограничение частоты sms-unsubscribe,
три файла тестов, три миграции (на прод не выкатывались).

Осталось нетронутым: стоп-лист самого клиента «Не писать этим» и общий стоп-лист
портала — это другое, их владелец не отменял.

Строки приёмочного листа 1.6-1.13 срезаны, записано в «Чего эта работа НЕ делает» п.14
и в журнал вопросов В-30. Возражение про 38-ФЗ высказано владельцу и им отклонено.
Всё удалённое лежит в истории: коммиты 7aa30833 и 1dece3a2.

ClientSms 140/140, приём лидов 17/17.
2026-07-27 18:58:48 +03:00
Дмитрий 1dece3a2b4 feat(смс-клиент): хвост «Отказ: liderra.ru/s/…» в тексте — цена считается уже с ним
Галочка «добавить возможность отказа» в рассылке, по умолчанию включена (спека §9.2).
Токен свой у каждого получателя, поэтому текст собирается на каждый номер отдельно
в момент отправки. Длина хвоста постоянная — число кусков и цена считаются один раз,
при создании рассылки, УЖЕ с хвостом: клиент видит ту длину, за которую заплатит.

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

Строки приёмочного листа 1.10-1.13 (серверная часть). Защита проверена вырезанием:
считать цену без хвоста — 2 красных теста, не дописывать хвост при отправке — 1.

ClientSms 146/146, приём лидов 17/17, phpstan по своим файлам чисто.
2026-07-27 18:22:26 +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
Дмитрий 5d423be226 feat(смс-клиент): джоб отправки — деньги через кошелёк, песочница, retry
SendClientSmsCampaignJob: single-tenant под crm_app_user (SET LOCAL перед
каждой операцией; сеть вне транзакций). Списание/снятие заморозки через
AdWalletService (канал sms, рубли, идемпотентно по external_key). Песочница
не двигает деньги. failed() снимает заморозку.

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