Заказ из города, выключенного для звонков (Настройки → Коммуникации →
Автодозвон → 📍 Локации), больше не выглядит как «звонка не было»:
- autocall_status(og) отдаёт новый статус city_disabled (порядок: ручной
запрет → локация выключена → исключение по номеру → журнал звонков);
- бейдж в реестре: 🚫 не звоним, виден и в завершённом заказе;
- фильтр «Статус автодозвона»: «Не звоним (город)»;
- карточка «Автодозвон»: пояснение «Не звоним — <город>», кнопка
«Позвонить сейчас» заблокирована (планировщик такие заявки пропускает);
- сводка заказа и шаг «Звонок» в цепочке: «не звоним (локация выключена)»;
- в детальную выборку группы добавлены autocall_status/exclusion_reason;
- SW-кэш static-v129.
На SMS выключение локации не влияет — уведомления продолжают уходить.
Владелец: «нужно видеть, согласование прошло через пуш/смс/звонок/вручную через
менеджера» и «в колонке с именем клиента пишется просто «Согласовано» — непонятно
как и кем». Плюс в карточке «завтра/послезавтра» читалось как дата заказа.
Источник правды — order_groups.delivery_date_source: manual (логист), autocall
(робот), и теперь push/sms/messenger/client от клиентской страницы. Раньше клиентское
подтверждение канал не писало вовсе — такие записи разбираются по confirmed_at
приглашения (канал «link» → «По ссылке»).
- миграция 20260929_confirmation_channel.sql: confirm_delivery_choice_by_token
принимает p_src и сохраняет канал в delivery_date_source + action_logs.details;
- клиентская страница читает ?src= из ссылки и прокидывает в RPC (SMS-ссылка уже
содержит src=sms);
- orderGroupViews: resolveConfirmChannel/confirmChannelMeta — единый источник подписей
канала, плюс invitationConfirmedAt в репозитории (и в списке, и в карточке);
- реестр доставок/самовывозов: под статусом чип «как согласовано» (📞 звонок робота,
✉️ SMS-ссылка, 🔔 push, 🧑💼 логист, 🔗 по ссылке);
- карточка заказа: рядом с «Согласовано» тот же чип;
- AutocallStatusCard: «робот предлагал: завтра или послезавтра» вместо голого
«завтра/послезавтра» (читалось как дата заказа);
- Аналитика: в «Пути заказа» блок «Как согласовали дату» — полосы по каналам
(за 30 дней: логист 135, по ссылке 20, звонок робота 7, push 0).
Тесты: orderGroupViews +4, OrderPathFunnelBlock +1 (37 зелёных в этих файлах). SW v128.
Владелец: «а путь заказа можно сдать какой-то красивой воронкой?»
В admin_comms_funnel добавлен блок order_path: этапы (групп в работе → первое SMS →
дата согласована → выдано по цепочке), честная плашка «мимо цепочки» (выдано без даты,
платное хранение, отмены), источники даты (менеджер/робот/без даты) и разбивка
самовывоз/доставка. Считается так, что водитель без даты не едет: последний этап —
это выданные С датой, поэтому воронка монотонная.
OrderPathFunnelBlock рисует воронку штатным recharts FunnelChart (градиент по этапам,
подписи «значение · %», тултип с конверсией шага), под ней — легенда теми же цифрами
(на телефоне воронку читать тяжело), чипы конверсий между ступенями, плашка «мимо
цепочки» и чипы источников/типа заказа. Блок стоит над воронкой коммуникаций.
За 30 дней: 396 → первое SMS 340 (85.9%) → дата 161 (47.4% от SMS) → выдано по цепочке
134 (83.2%); мимо цепочки 204; дата от менеджера 135, от робота 6; самовывоз 235,
доставка 161. Тест: OrderPathFunnelBlock.test.jsx (6 проверок). SW v127.
На 390px таблицы «Не поднимают трубку» и «Открыли ссылку, но не подтвердили»
уезжали вправо (статус и телефон не видны без прокрутки). На мобильном они
раскладываются списком: телефон и число попыток в строку, город/тип/статус и
дата — ниже мелким. В таблице попыток процент ответов уходит во вторую строку,
чтобы колонка «Не взяли» влезла. Тест на мобильную раскладку (8 проверок). SW v125.
Владелец: «нужно видеть, как всё происходит: push отправили — реакции не было, потом SMS,
потом звонок; с какого раза человек ответил на звонок, кто вообще не поднимает трубку».
Один RPC admin_comms_funnel(p_days, p_date_from, p_date_to) — период считается как у
admin_automation_economics (30 = группы, созданные за 30 дней; 0 = всё время) — отдаёт:
этапы цепочки по когорте (группы → push → первое SMS → открыли ссылку → позвонили →
взяли трубку → согласовано → в ручную → выдано), разбивку звонков по попыткам
(всего/взяли/согласовали/не взяли + % ответов), каналы подтверждения (робот / менеджер /
ссылка), список «не поднимают трубку» и «открыли ссылку, но не подтвердили», и «silent» —
кого не достали ни ссылкой, ни звонком.
Блок CommsFunnelBlock на «Аналитике» под KPI-карточками, те же табы периода, mobile-first,
время в МСК. За 30 дней: 396 групп, первое SMS 340, открыли ссылку 1, звонили 26,
взяли трубку 14, согласовано 128, в ручную 22, выдано 357; 265 (66.9%) не достали никак.
С попыток: 1-я — 42 ответа из 64, 2-я — 2 из 6, 3-я — 4 из 4.
Тест: src/components/admin/analytics/CommsFunnelBlock.test.jsx (7 проверок, фикстура по
реальному ответу RPC). SW: v123 -> v124.
В узкой колонке «Вторая половина дня» переносилась посреди фразы («Вторая /
половина дня»). Дата — первой строкой, половина дня — второй, мелким. SW: v123.
Владелец: «в таблице всех доставок и самовывоза, если согласована доставка, дата
и время указаны, а если самовывоз — нет; нужно и самовывоз выводить».
Причина: колонка «Дата» (LogisticsReadinessBoard) читала только delivery_date/
delivery_time, а у самовывоза дата и половина дня лежат в pickup_date/
pickup_time_slot — поэтому строка самовывоза показывала «—», хотя слот согласован.
Тот же пропуск был в фильтре по периоду и группировке по датам: getDeliveryDate()
смотрела только на delivery_date, и при выбранном периоде заказы самовывоза
пропадали из реестра (падали в дату заказа).
Введены общие хелперы в orderGroupViews: isPickupGroup, getGroupSlotDate,
getGroupSlotTime (камелкеис и snake_case). Реестр показывает дату и время слота
по типу заказа, сортировка — по этой же показанной дате, фильтр по периоду и
группировка — тоже по ней. Подпись колонки «Доставка» → «Дата» (в колонке теперь
и дата выдачи).
Попутно поправлено устаревшее ожидание в тесте (getOrderGroupDisplayStatusLabel
для черновика ждало «draft», подпись локализована в «Черновик») — тест падал и до
этой правки.
SW: v121 -> v122.
Владелец: «в субботу звонки не должны происходить, всё это настраивается из
админки». В панели «Автодозвон» в блоке «Окно звонков» были только часы, а дни
(work_days) править из админки было нечем — их приходилось менять в БД, хотя
планировщик именно по ним и решает, звонить ли сегодня.
Добавлены таблетки дней Пн–Вс тем же механизмом и в том же виде, что в панели
SMS-кампаний: пустой набор не подменяется дефолтом (снял все дни — значит
звонков нет, и в панели это видно). Подсказка в панели теперь честно описывает
поведение: вне окна робот не звонит вообще, попытка уходит на ближайшее
рабочее утро.
Харнесс check_autocall_window.py считает ожидания ИЗ НАСТРОЕК (часы + дни),
поэтому проверка не устаревает при смене графика.
SW: v120 -> v121.
В блоке «История действий по заказу» автодозвон не был виден в цепочке шагов —
там были только «Добавлен», «SMS», «Согласовано» и далее, хотя робот звонит по
этим же заказам. Теперь шаг «Звонок» идёт сразу после SMS в обеих цепочках
(доставка и самовывоз) и имеет собственное состояние: ✓ пройден, если робот
согласовал слот; янтарный — если звонили без результата; серый — если звонков
не было. Шаг не считается пройденным только потому, что заказ уже согласован
(согласовать могли по ссылке из SMS, без звонка). Подсказка при наведении —
последний исход и число звонков.
SW: v119 -> v120.
Клиентская страница согласования:
- приглашение теперь отдаёт deliveryType и pickupCode (public view) — до этого
страница не знала, что заказ самовывозный, и говорила «Доставка заказа /
Согласование доставки» даже для самовывоза; в шапке и hero-тексте выбор по типу
- в блоке с кодом: «Ваш номер доставки 010796» + «Ваш код выдачи 7598»
(номер берём из состава заказа, «ТП\-010796» → «010796»)
- ссылка на заказ теперь «Доставка ТП-010796» вместо «Счет ТП\-010796»,
экранированный дефис из обмена с 1С больше не показывается
SMS: в шаблоны добавились переменные {order}/{номер}/{номер_доставки} (номер
доставки) и {code}/{код}/{код_выдачи} (код выдачи) — работают и в шаблоне
кампании, и в тексте профиля города. Дефолтные тексты самовывоза (и значения в
БД, см. supabase/sms_pickup_code_placeholders.sql) теперь содержат номер и код;
в админке у полей появилась памятка по переменным и предпросмотр с подстановкой.
service-worker v118 → v119
Раньше текст был зашит в кампании и всегда говорил про доставку; настраиваемый
в админке sms_text_template не читался скриптами вообще. Сначала выяснилось,
что всем 909 pickup-группам уходило «Согласуйте дату доставки по ссылке».
- sms_campaign_settings.sms_text_template_pickup (новая колонка) + значения
sms_text_template = «…дату доставки…», pickup = «…дату самовывоза…»
(supabase/sms_text_templates_pickup.sql = миграция с фактическими текстами)
- sms_first_campaign / sms_second_campaign / sms_paid_storage_campaign:
build_sms_text(group, settings) — шаблон выбирается по og.delivery_type,
{link} подставляется ссылкой; приоритет профиль города (режим «Уведомление»)
→ шаблон кампании → дефолт скрипта
- вторая SMS и платное хранение тоже получили pickup-вариант (кампании были
выключены, поэтому в текстах жил только «доставка»)
- SmsCampaignPanel: два поля ввода (📦 Доставка / 🏪 Самовывоз) + длины и
предпросмотр с подставленной ссылкой + пояснение приоритета
- service-worker v117 (обязательный бамп при деплое фронта)
- activate: delete ALL caches except current STATIC/RUNTIME (was keeping every
previous construction-delivery-* cache forever → 3GB storage, 30-40s caches.match)
- fetch: only cache same-origin static assets (js/css/img/fonts); API/data and
cross-origin requests go straight to network, never cached
- bump to v115