Перед каждым списанием джоб проверяет, что деньги есть. Без этого
AdWalletService::charge при недоборе не бросает исключение, а прижимает
баланс к нулю: часть рассылки уходила бы бесплатно, а клиент видел бы
пустой кошелёк без объяснения.
Доступное этой рассылке = баланс − заморожено + СВОЯ активная заморозка.
Наивное «баланс − заморожено» остановило бы рассылку на первом же номере
при полном кошельке — вся смета заморожена при создании (ловушка В-41,
доказана вырезанием: тест-страховка краснеет).
Кончились деньги — номер пишется в журнал как «не хватило денег», рассылка
получает статус «остановлена» с причиной no_funds, заморозка снимается.
Экран разбирает причину: «Остановлено: закончились деньги. Ушло N из M,
списано X ₽».
Живой прогон нашёл то, чего не видели тесты (В-48): статус в колонке рядом
писал «остановлена вами» и для денежной остановки. Починено, тест расширен
на всю ячейку.
Строка приёмочного листа 2.4. Тесты: ClientSms 147/147, приём лидов 17/17,
фронт 1648, phpstan 0, pint чисто.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>