fix реклама Яндекса: принятые объявления сами уходят в показ — модерация приняла, это ещё не показ

У объявления два независимых признака: вердикт модерации (Status) и идёт ли
показ (State). Созданное программой объявление рождается выключенным, и
включение кампании его не поднимает — команды поднять сами объявления в коде
не было вообще.

Живая приёмка 31.07.2026, кампания 713175197: принята и включена, Яндекс на
уровне кампании пишет «Идут показы», а внутри все 15 объявлений выключены.
Показов ноль при замороженных у клиента 3333.36 руб.

Опросчик теперь чинит по СОСТОЯНИЮ из ответа Яндекса, а не по переходу
статуса: собирает объявления со статусом «принято» и выключенным показом и
зовёт ads.resume. Так самовосстанавливается и уже запущенная кампания — по
переходу она осталась бы выключенной навсегда, потому что «принято» у нас
записано давно. Состояние Яндекс присылал в каждом ответе и раньше, опросчик
его просто выбрасывал.

Проверено вырезанием: без починки новый тест краснеет. Реклама 349 из 349.
This commit is contained in:
Дмитрий
2026-07-31 09:13:27 +03:00
parent 9df7846fd6
commit f3d9812e6c
4 changed files with 107 additions and 4 deletions
@@ -522,3 +522,61 @@ it('пока разведка не сходила, заглушка клиент
expect($first)->not->toBeNull()
->and($first->body)->toContain('причину не назвал');
});
/**
* 🔴 Модерация приняла это ещё НЕ показ. У объявления два независимых признака:
* вердикт (Status) и включён ли показ (State). Созданное программой объявление
* рождается выключенным, и включение кампании его не поднимает.
*
* Живая приёмка 31.07.2026: кампания принята и включена, Яндекс пишет «Идут показы»,
* а внутри все 15 объявлений выключены показов ноль при замороженных деньгах клиента.
*/
it('включает принятые объявления, которые Яндекс держит выключенными', function () {
configureYandexForModeration();
Http::fake(['*/json/v5/ads' => Http::response(['result' => ['Ads' => [
['Id' => 7701, 'State' => 'OFF', 'StatusClarification' => null, 'Status' => 'ACCEPTED'],
['Id' => 7702, 'State' => 'OFF', 'StatusClarification' => null, 'Status' => 'ACCEPTED'],
]]])]);
makeModeratedCampaignWithBanners([7701, 7702]);
(new SyncCampaignModerationJob)->handle();
Http::assertSent(function ($request) {
$data = $request->data();
return ($data['method'] ?? null) === 'resume'
&& ($data['params']['SelectionCriteria']['Ids'] ?? []) === [7701, 7702];
});
});
/**
* Обратная сторона: лишний раз дёргать Яндекс нельзя. Объявление, которое уже
* показывается, включать не за чем иначе на каждом обходе улетал бы холостой запрос.
*/
it('не дёргает включение у объявления, которое и так показывается', function () {
configureYandexForModeration();
Http::fake(['*/json/v5/ads' => Http::response(['result' => ['Ads' => [
['Id' => 7703, 'State' => 'ON', 'StatusClarification' => null, 'Status' => 'ACCEPTED'],
]]])]);
makeModeratedCampaignWithBanners([7703]);
(new SyncCampaignModerationJob)->handle();
Http::assertNotSent(fn ($request) => ($request->data()['method'] ?? null) === 'resume');
});
/** Отклонённое объявление включать нельзя: показывать его Яндекс всё равно не будет. */
it('не включает отклонённое объявление', function () {
configureYandexForModeration();
Http::fake(['*/json/v5/ads' => Http::response(['result' => ['Ads' => [
['Id' => 7704, 'State' => 'OFF', 'StatusClarification' => 'Нельзя', 'Status' => 'REJECTED'],
]]])]);
makeModeratedCampaignWithBanners([7704]);
(new SyncCampaignModerationJob)->handle();
Http::assertNotSent(fn ($request) => ($request->data()['method'] ?? null) === 'resume');
});