diff --git a/docs/superpowers/2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md b/docs/superpowers/2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md index 39d565f6..f2dbb00e 100644 --- a/docs/superpowers/2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md +++ b/docs/superpowers/2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md @@ -454,6 +454,97 @@ webm/mkv/mpeg/wmv проверяются формат и вес, остальн «Выберите, кому показывать, — посчитаем охват.» Но выбор-то уже сделан — не хватает самих номеров. Поломкой не выглядит, но фраза не про ту ситуацию. Мелочь, **решение за владельцем.** +#### 🔴 Находка 4 — «Моя база номеров» ведёт в никуда: загрузить базу некуда + +**Нашёл владелец за полминуты, глядя на экран** (02.08.2026): выбрал «Моя база номеров», +увидел ноль и спросил — «а куда эту базу загружать?». + +Ответ: **никуда.** Пункт читает `client_tg_contacts`. Замер по всему проекту: в эту таблицу +**не пишет ни одна строка кода**. Есть только миграция, модель и чтение в +`TelegramAudienceService::fromBase()`. Ни экрана загрузки, ни серверной ручки (в телеграм-модуле +их 10 — `routes/web.php:100–116`, среди них нет ни одной про контакты), ни переноса из сделок, +ни задания-заливщика. + +Значит, у **любого** клиента этот пункт всегда покажет ноль — это не пустота демо-организации. + +**Почему не поймали 292 зелёных теста ClientTg:** каждый тест сам вписывает контакты +(`Contact::create(...)` в `AudienceServiceTest.php:67`, `:116`, `:132` и далее). Проверяется +«если контакты есть — охват считается верно», а до состояния «контакты появились» тест не +доходит, потому что дойти туда нечем. Ровно класс «голый шов»: тест СОБИРАЕТ состояние руками +вместо того, чтобы ДОЙТИ до него. + +Рабочих путей из трёх — два: «Свежие сделки за период» (читает `deals`) и «Свой список номеров» +(номера печатаются в поле, уходят в `client_tg_campaign_phones`). Оба проверены живьём. + +**Решение за владельцем — две дороги:** + +1. **Убрать пункт** с экрана (и из авто-правила, если он там есть) — честно, быстро, ничего не + обещает впустую. +2. **Сделать загрузку базы** — экран заливки файла + ручка + разбор номеров + стоп-лист. Это + отдельная пачка, не мелкая правка. + +🔴 **До решения этот пункт нельзя катить на боевой в нынешнем виде:** клиент выберет «свою +базу», увидит ноль и решит, что портал потерял его клиентов. + +#### 🔴 Находка 5 — с медиа наша наценка съедается, а на видео уходит в минус + +**Вскрылось из вопроса владельца 02.08** «цена на 40% больше, а в течение какого срока +показываться будет?». + +**Что клиент платит нам.** Ступенчатый тариф `client_tg_tariffs` × наценка +`TG_MARKUP = 1.40` (+40%), считает `TelegramTariffService::clientEstimateRub($impressions)`. +Ступени (₽ за показ, до наценки): 0.45 / 0.42 (от 1000) / 0.40 (от 5000) / 0.38 (от 10000) / +0.36 (от 50000). С наценкой это **630 / 588 / 560 / 532 / 504 ₽ за тысячу**. + +**Что берёт МТС.** Замер живьём в кабинете 02.08 (см. «💰 Медиа удорожает показы»): +**с изображением 600 ₽ за тысячу, с видео 680 ₽**. Базовая ставка без медиа — по комментарию +в `bots/mts-telegram-ads/src/cabinet.js:556` ≈400 ₽ за тысячу; **живьём не переподтверждено**. + +**Беда:** `clientEstimateRub()` принимает ТОЛЬКО число показов. Медиа на цену для клиента +**не влияет вообще** — ни в оценке, ни в списании (`CampaignController.php:204`, `:307`). + +| Объём | Клиент платит нам | МТС без медиа ≈400 | МТС с картинкой 600 | МТС с видео 680 | +|---|---|---|---|---| +| до 1000 | 630 | +230 | **+30** | **−50** | +| от 5000 | 560 | +160 | **−40** | **−120** | +| от 50000 | 504 | +104 | **−96** | **−176** | + +То есть на кампании с видео портал платит МТС больше, чем берёт с клиента. Чем крупнее +кампания, тем больше убыток. + +При этом подсказка у поля медиа честно говорит клиенту «с картинкой показ стоит дороже — +600 ₽ за тысячу, с видео 680 ₽», а счёт ему выставляется тот же самый. Обещание и счёт +разошлись в разные стороны. + +**Решение за владельцем — дороги:** + +1. **Считать медиа в цене**: передавать в `clientEstimateRub()` признак медиа и брать за такие + показы отдельную ставку (тариф со ступенями «без медиа / картинка / видео»). +2. **Оставить как есть** — сознательно доплачивать за медийные кампании ради привлекательности. +3. **Не давать медиа**, пока цена не разведена. + +🪤 Ловушка: пока `TG_SANDBOX=false`, это **живые деньги**. Первая же крупная кампания с видео +уйдёт в минус молча — портал нигде не сравнивает свою цену с ценой МТС. + +#### ⏱ Срок показа: 7 дней, а не «пока лимит не кончится» + +Ответ на второй вопрос владельца. Портал сроком **не управляет вообще** — в +`client_tg_campaigns` нет ни одного поля с датой, и в задании роботу +(`RunTelegramCampaignJob.php:109–120`) передаются только текст, ссылка, заголовок, лимит, +категория ОРД и файлы. Даты там нет. + +Срок задаёт кабинет МТС **своими значениями по умолчанию**, робот их не трогает +(`bots/mts-telegram-ads/src/cabinet.js:555–563`): **показать каждому 1 раз, даты «сегодня → ++7 дней», автозапуск после модерации включён.** + +Значит: **окно показа — 7 дней**, каждый человек видит объявление **один раз**, поэтому всего +показов примерно столько же, сколько людей в аудитории. «Лимит на объявление, ₽» — это +**потолок-предохранитель**, а не то, что растягивает или сокращает показ. + +🔴 На экране этого не написано нигде. Клиент не знает ни про 7 дней, ни про «один раз +человеку». **Решение за владельцем:** дописать в подсказку к лимиту или отдельной строкой в +сводке шага «Проверка и отправка». + #### Что осталось за приёмкой глазами **Боевой не смотрели.** Всё выше — локальная версия. На боевом экраны выглядят иначе как