- Pa11y: в конфиг добавлены три рекламных экрана — клиентский Яндекс,
рекламный кошелёк и админский расход/маржа.
- Чипы статусов «Черновик» и «На паузе» были на обычном grey — контраст
2.42:1 при норме WCAG 2.1 AA 4.5:1. Заменены на grey-darken-2, после
чего оба клиентских рекламных экрана дают 0 ошибок.
- CampaignLauncherTest коммитил кампании в статусе pending_moderation
с сегментом и сотней телефонов и не откатывал их. При полном прогоне
SyncCampaignAudienceJob перечисляет кампании всех тенантов через
pgsql_supplier и подхватывал эти хвосты — SyncCampaignAudienceJobTest
падал на «ни одного обращения к Яндексу». Добавлен DatabaseTransactions.
Замер: раньше после прогона папки оставалось 2 живых кампании, теперь 0.
Полный прогон: было 5 падений, стало 3 — оба рекламных ушли.
- Находка про контраст палитры портала вынесена отдельным документом:
зелёный 4.25 и жёлтый 2.25 на белом не проходят AA, но это брендбук
и уже на бою — решение за владельцем.
Реклама 156/156. Боевого не касается, в main не влито.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Задача 10, фиксы найденного ревьюером в денежном ядре Части 4:
- CampaignLauncher при запуске ставит paid_impressions = смета показов. Это потолок
биллинга: счётчик показов ограничивает списание сметой и завершает кампанию по её
достижении. Раньше поле не ставилось никогда → списание шло без потолка до
SpendLimit ×guard и кампания не завершалась.
- CampaignImpressionCharger держит charged_client_rub монотонным high-water: при
просадке накопительного отчёта Директа база не опускается → нет двойного списания
при последующем росте.
- YandexDirectClient getCampaignImpressions шлёт уникальный ReportName на каждый
вызов, иначе Яндекс отдаёт кэш ALL_TIME-отчёта и списание замирает недобор.
- effectiveCpm без float, через bcmath.
Реклама-модуль 196/196 зелёный. Заморозка при завершении не снимается это отдельная
пред-существующая дыра модели кошелька release не зовётся нигде вынесено владельцу.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Задача 7 плана «реклама за показы». CampaignLauncher переписан под медийную
кампанию CPM: дешёвые проверки до трат денег рубильник, номер креатива, адрес
сайта, смета показов, затем аудитория не меньше 100, деньги через bcmath с
наценкой, цепочка сегмент → retargeting → CPM-кампания → CPM-группа →
медиа-таргет → баннер по готовому креативу, заморозка клиентской суммы,
статус на модерации.
Колонки ad_campaigns.landing_url и yandex_ad_id, модель, журнал схемы v9.02.
Наценка и yandex_cost_rub клиенту не видны. Реклама-модуль 190/190 зелёный.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Часть 5c — баннеры: клиент грузит свой готовый файл на каждый из 15 размеров вместо автогенерации из одной картинки. Частичное утверждение флагом included, замена и удаление отдельного баннера, валидация точного размера и веса. Админ-поле цены за 1000 показов. Пример CSV для скачивания и подъём лимита загрузки.
Часть 5d — два режима сбора аудитории. Авто: скользящее окно, обновляется ежедневно, только контакты системы. Ручной: снимок сделок за период плюс свой список номеров и срок показа. Клиент сам задаёт цену за 1000 показов с дефолтом из админки. Наценка настраивается в админке, по умолчанию 40 процентов, в Директ уходит меньше, клиенту не видна нигде.
Миграции: ad_campaign_banners += included; ad_campaigns += mode/snapshot_from/snapshot_to/run_days/client_cpm_rub; ad_settings += ad_margin_percent. RLS-ревью PASS на всех миграциях. Backend 166 тестов, фронт 123 теста, сборка чистая. Маржа и yandex_cost_rub клиенту не сериализуются.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>