fix(autocall): убраны сценарии «0» и «9» из диалога и из презентации; пауза между звонками 1 час; граница половины дня 13:00; в презентации готовность по обмену с 1С, без готовности в субботу

This commit is contained in:
Hermes 2026-09-16 10:36:01 +02:00
parent 833d2dcd44
commit aeb4bbf63f
5 changed files with 31 additions and 40 deletions

View File

@ -64,7 +64,7 @@ on_interrupt () {
if [ -z "$OUTCOME" ]; then if [ -z "$OUTCOME" ]; then
if [ -n "$HALF" ]; then if [ -n "$HALF" ]; then
OUTCOME=no_input_half # день выбрал, время не назвал OUTCOME=no_input_half # день выбрал, время не назвал
elif [ -n "$DAY" ] && [ "$DAY" != "0" ] && [ "$DAY" != "9" ]; then elif [ -n "$DAY" ] && [ "$DAY" != "0" ]; then
OUTCOME=no_input_half # день выбран, до времени не дошло OUTCOME=no_input_half # день выбран, до времени не дошло
elif [ -z "$ANY_DIGIT" ] && [ "$RING_SEC" -ge 20 ]; then elif [ -z "$ANY_DIGIT" ] && [ "$RING_SEC" -ge 20 ]; then
OUTCOME=no_answer # объявление оператора: человека не было OUTCOME=no_answer # объявление оператора: человека не было
@ -114,7 +114,7 @@ save () {
# Отправляем в БД сразу при финальном исходе (клиент может бросить трубку # Отправляем в БД сразу при финальном исходе (клиент может бросить трубку
# в любой момент — результат должен успеть уйти). Идемпотентность по uniqueid. # в любой момент — результат должен успеть уйти). Идемпотентность по uniqueid.
case "$OUTCOME" in case "$OUTCOME" in
confirmed|confirmed_kept_previous|confirmed_silent|no_answer|no_input_day|no_input_half|operator|opt_out|invalid_number|error) confirmed|confirmed_kept_previous|confirmed_silent|no_answer|no_input_day|no_input_half|operator|invalid_number|error)
send_result ;; send_result ;;
esac esac
} }
@ -236,7 +236,6 @@ if [ "$TYPE" = "pkp" ] && [ -n "$PICKUP_DAY1" ]; then
fi fi
CHOSEN_DAY=$DIGIT CHOSEN_DAY=$DIGIT
case "$CHOSEN_DAY" in case "$CHOSEN_DAY" in
9) play "ru/ss_optout"; OUTCOME=opt_out; break ;;
1|2|3) [ "$CHOSEN_DAY" -le "$DAYS" ] || CHOSEN_DAY="" ;; 1|2|3) [ "$CHOSEN_DAY" -le "$DAYS" ] || CHOSEN_DAY="" ;;
*) CHOSEN_DAY="" ;; *) CHOSEN_DAY="" ;;
esac esac
@ -320,7 +319,6 @@ if [ "$TYPE" = "dlv" ] && [ -n "$DELIVERY_DAY1" ]; then
wait_digit 9000 wait_digit 9000
CHOSEN_DAY=$DIGIT CHOSEN_DAY=$DIGIT
case "$CHOSEN_DAY" in case "$CHOSEN_DAY" in
9) play "ru/ss_optout"; OUTCOME=opt_out; break ;;
1|2) [ "$CHOSEN_DAY" -le "$DLV_DAYS" ] || CHOSEN_DAY="" ;; 1|2) [ "$CHOSEN_DAY" -le "$DLV_DAYS" ] || CHOSEN_DAY="" ;;
*) CHOSEN_DAY="" ;; *) CHOSEN_DAY="" ;;
esac esac
@ -373,7 +371,7 @@ while [ -z "$OUTCOME" ]; do
if [ "$SCENARIO" = "A" ]; then if [ "$SCENARIO" = "A" ]; then
digit "ru/ss_a_day_${TYPE}" digit "ru/ss_a_day_${TYPE}"
DAY=$DIGIT DAY=$DIGIT
if [ "$DAY" != "1" ] && [ "$DAY" != "2" ] && [ "$DAY" != "0" ] && [ "$DAY" != "9" ]; then if [ "$DAY" != "1" ] && [ "$DAY" != "2" ] && [ "$DAY" != "0" ]; then
digit "ru/ss_a_day_${TYPE}" digit "ru/ss_a_day_${TYPE}"
DAY=$DIGIT DAY=$DIGIT
fi fi
@ -382,9 +380,7 @@ while [ -z "$OUTCOME" ]; do
fi fi
save save
if [ "$DAY" = "9" ]; then if [ "$DAY" != "1" ] && [ "$DAY" != "2" ]; then
play "ru/ss_optout"; OUTCOME=opt_out; break
elif [ "$DAY" != "1" ] && [ "$DAY" != "2" ]; then
if [ -z "$ANY_DIGIT" ] && [ "$RING_SEC" -ge 20 ]; then if [ -z "$ANY_DIGIT" ] && [ "$RING_SEC" -ge 20 ]; then
OUTCOME=no_answer; break # объявление оператора, человека не было OUTCOME=no_answer; break # объявление оператора, человека не было
fi fi

File diff suppressed because one or more lines are too long

View File

@ -81,8 +81,8 @@ SLIDES = [
], ],
"full": {"title": "Текущие значения в системе", "accent": WARN, "items": [ "full": {"title": "Текущие значения в системе", "accent": WARN, "items": [
"Склад: пн–пт, 09:00–17:30, половина дня до 13:00, «сегодня» предлагаем до 15:30", "Склад: пн–пт, 09:00–17:30, половина дня до 13:00, «сегодня» предлагаем до 15:30",
"Доставка: пн–пт (суббота — выходной) 09:00–21:00, половина дня до 15:00", "Доставка: пн–пт (суббота — выходной) 09:00–21:00, половина дня — граница 13:00",
"Утро и вечер: до 13:00 робот спросит, какая половина дня удобнее; после 13:00 — сразу зафиксирует вторую", "Граница половины дня — 13:00 для обоих каналов: звоним после 13:00 — говорим про вторую половину дня, первую уже не предлагаем",
]}, ]},
}, },
{ {
@ -94,16 +94,15 @@ SLIDES = [
["Заказ готов", "Что предложит робот", "Почему так"], ["Заказ готов", "Что предложит робот", "Почему так"],
["в понедельник", "пн, вт, ср", "три рабочих дня подряд"], ["в понедельник", "пн, вт, ср", "три рабочих дня подряд"],
["в пятницу", "пт, пн, вт", "суббота и воскресенье — выходные склада"], ["в пятницу", "пт, пн, вт", "суббота и воскресенье — выходные склада"],
["в субботу", "пн, вт, ср", "сам выходной не предлагаем"],
["в пятницу после 15:30", "пн, вт", "«сегодня» больше не предлагаем (настройка)"], ["в пятницу после 15:30", "пн, вт", "«сегодня» больше не предлагаем (настройка)"],
["в понедельник после 13:00", "пн, вт, ср", "половину дня не спрашиваем — сразу вторая"], ["в понедельник после 13:00", "пн, вт, ср", "половину дня не спрашиваем — сразу вторая"],
["неделю назад", "звонка нет", "окно прошло — звонить не о чем"], ["заказ готов давно", "звонка нет", "окно самовывоза прошло — звонить не о чем"],
], ],
"widths": [200, 240, 428], "widths": [200, 240, 428],
}, },
"full": {"title": "Две настройки, которые это двигают", "accent": ACCENT, "items": [ "full": {"title": "Две настройки, которые это двигают", "accent": ACCENT, "items": [
"«Сегодня предлагаем до» (сейчас 15:30) — позже этого времени сегодняшний день исчезает из звонка и из ссылки", "«Сегодня предлагаем до» (сейчас 15:30) — позже этого времени сегодняшний день исчезает из звонка и из ссылки",
"«Первая половина — до» (сейчас 13:00) — после неё робот не спрашивает половину дня, сразу пишет вторую", "Граница половины дня (13:00): звоним после 13:00 — робот говорит про вторую половину дня, первую не предлагает",
]}, ]},
}, },
{ {
@ -115,7 +114,6 @@ SLIDES = [
["Заказ готов", "Что предложит робот"], ["Заказ готов", "Что предложит робот"],
["в понедельник", "вторник и среда"], ["в понедельник", "вторник и среда"],
["в пятницу", "понедельник и вторник (в субботу не возим)"], ["в пятницу", "понедельник и вторник (в субботу не возим)"],
["в субботу", "понедельник и вторник"],
["в четверг, звонок в пятницу", "пятница и понедельник"], ["в четверг, звонок в пятницу", "пятница и понедельник"],
["в пятницу, звонок в понедельник", "понедельник и вторник"], ["в пятницу, звонок в понедельник", "понедельник и вторник"],
], ],
@ -124,7 +122,7 @@ SLIDES = [
"full": {"title": "Ключевое отличие от самовывоза", "accent": ACCENT2, "items": [ "full": {"title": "Ключевое отличие от самовывоза", "accent": ACCENT2, "items": [
"В день готовности доставку не предлагаем никогда — только следующие рабочие дни", "В день готовности доставку не предлагаем никогда — только следующие рабочие дни",
"Суббота и воскресенье в графике доставки — выходные, их робот не назовёт", "Суббота и воскресенье в графике доставки — выходные, их робот не назовёт",
"Время доставки — половина дня: до 15:00 или после (границу задаёт админка)", "Половина дня — граница 13:00: звоним после 13:00, значит говорим про вторую половину дня",
]}, ]},
}, },
{ {
@ -158,15 +156,16 @@ SLIDES = [
["1", "первый предложенный день", "робот переходит к выбору половины дня"], ["1", "первый предложенный день", "робот переходит к выбору половины дня"],
["2", "второй предложенный день", "робот переходит к выбору половины дня"], ["2", "второй предложенный день", "робот переходит к выбору половины дня"],
["3", "третий день (только самовывоз)", "робот переходит к выбору половины дня"], ["3", "третий день (только самовывоз)", "робот переходит к выбору половины дня"],
["1 / 2", "половина дня", "первая половина / вторая половина"], ["1 / 2", "половина дня", "первая половина / вторая (после 13:00 — только вторая)"],
["9", "отказ от звонков", "робот больше не позвонит; в карточке — «отказ от звонков»"], ["2 после подтверждения", "изменить выбор", "робот спрашивает день и время заново — один раз"],
["0 и прочие", "не принимаются", "вопрос повторяется; соединения с оператором нет"], ["любая другая цифра", "не принимается", "вопрос повторяется один раз; после двух таких звонков — менеджеру"],
], ],
"widths": [110, 240, 518], "widths": [176, 200, 492],
}, },
"full": {"title": "Отдельно про «0»", "accent": DANGER, "items": [ "full": {"title": "Что происходит после подтверждения", "accent": ACCENT2, "items": [
"Раньше в диалоге была попытка «позвать менеджера», но она никогда не срабатывала (нажатие не отличалось от тишины)", "Робот называет выбранный день и половину дня вслух — клиент слышит, что именно записано",
"Эту ветку убрали: живого соединения с оператором у робота нет, менеджер подключается только задачей из системы", "Дальше один раз предлагает изменить выбор: нажатие 2 — и день со временем спрашивают заново",
"Если клиент ничего не меняет, разговор завершается, а выбор уходит в заказ",
]}, ]},
}, },
{ {
@ -180,7 +179,7 @@ SLIDES = [
"Живой человек почти всегда берёт трубку раньше, поэтому правило надёжно отделяет автоответчик", "Живой человек почти всегда берёт трубку раньше, поэтому правило надёжно отделяет автоответчик",
]}, ]},
{"title": "Что происходит дальше", "accent": ACCENT, "items": [ {"title": "Что происходит дальше", "accent": ACCENT, "items": [
"Повторный звонок — не раньше чем через 120 минут (настройка)", "Повторный звонок — не раньше чем через час (настройка)",
"Всего не больше 5 попыток на заказ (настройка)", "Всего не больше 5 попыток на заказ (настройка)",
"Звонит только в рабочие часы кампании: 09:00–20:00, дни — по настройке", "Звонит только в рабочие часы кампании: 09:00–20:00, дни — по настройке",
"Исчерпали попытки — заказ уходит менеджеру, автозвонок по нему останавливается", "Исчерпали попытки — заказ уходит менеджеру, автозвонок по нему останавливается",
@ -227,10 +226,10 @@ SLIDES = [
"Когда попытки закончились, автозвонок прекращается", "Когда попытки закончились, автозвонок прекращается",
"Заказ остаётся в списке у менеджера", "Заказ остаётся в списке у менеджера",
]}, ]},
{"title": "Отказ или неверный номер", "accent": DANGER, "items": [ {"title": "Неверный номер", "accent": DANGER, "items": [
"Цифра 9 — клиент просит больше не звонить: ставим запрет автоматически", "Если номер не существует или недоступен — задача менеджеру",
"Неверный номер — задача менеджеру", "Менеджер может поставить «Не звонить» — тогда робот этого клиента не беспокоит",
"Отказ клиента действует и на другие заказы: он в списке «не звонить»", "Такая отметка видна в карточке заказа и учитывается при обзвоне",
]}, ]},
], ],
"full": {"title": "Что видит менеджер в карточке заказа", "accent": ACCENT, "items": [ "full": {"title": "Что видит менеджер в карточке заказа", "accent": ACCENT, "items": [
@ -246,7 +245,7 @@ SLIDES = [
"cards": [ "cards": [
{"title": "По клиенту и заказу", "accent": ACCENT, "items": [ {"title": "По клиенту и заказу", "accent": ACCENT, "items": [
"Клиент сам подтвердил день по ссылке — звонок не нужен", "Клиент сам подтвердил день по ссылке — звонок не нужен",
"Клиент просил больше не звонить (нажал 9) или менеджер поставил «Не звонить»", "Менеджер поставил «Не звонить» — робот этого клиента не беспокоит",
"Номер в списке исключений — например, на МТС-диапазон звонки не идут", "Номер в списке исключений — например, на МТС-диапазон звонки не идут",
"Самовывоз: автообзвон по самовывозу включается отдельной галочкой (сейчас выключен)", "Самовывоз: автообзвон по самовывозу включается отдельной галочкой (сейчас выключен)",
]}, ]},
@ -273,7 +272,7 @@ SLIDES = [
["Дата", "конкретная дата, которую назвал робот (например, понедельник 21.09)"], ["Дата", "конкретная дата, которую назвал робот (например, понедельник 21.09)"],
["Время", "«первая половина дня» или «вторая половина дня»"], ["Время", "«первая половина дня» или «вторая половина дня»"],
["Источник", "«согласовано роботом» — видно, что выбор сделал клиент в звонке"], ["Источник", "«согласовано роботом» — видно, что выбор сделал клиент в звонке"],
["Статус", "«согласовано» / «нужен менеджер» / «отказ от звонков»"], ["Статус", "«согласовано» / «нужен менеджер» / «не звонить» (запрет ставит менеджер)"],
["Комментарий", "«Согласовано роботом: завтра, первая половина дня»"], ["Комментарий", "«Согласовано роботом: завтра, первая половина дня»"],
], ],
"widths": [200, 668], "widths": [200, 668],
@ -321,8 +320,8 @@ SLIDES = [
], ],
"full": {"title": "Что предлагаем уточнить до боевого запуска", "accent": ACCENT2, "items": [ "full": {"title": "Что предлагаем уточнить до боевого запуска", "accent": ACCENT2, "items": [
"Список праздников (или решение не учитывать их)", "Список праздников (или решение не учитывать их)",
"Нужен ли жёсткий график повторов по времени, кроме интервала 120 минут", "Нужен ли жёсткий график повторов по времени, кроме паузы в час",
"Оставить ли «0» без реакции — сейчас это просто неверная цифра", "Кто и когда разбирает заказы, которые робот не согласовал за 5 попыток",
]}, ]},
}, },
{ {
@ -334,9 +333,9 @@ SLIDES = [
"Ждёт 90 минут после SMS и звонит, если клиент не подтвердил день сам", "Ждёт 90 минут после SMS и звонит, если клиент не подтвердил день сам",
"Предлагает только рабочие дни по графику: самовывоз — включая сегодня, доставка — со следующего дня", "Предлагает только рабочие дни по графику: самовывоз — включая сегодня, доставка — со следующего дня",
"Спрашивает день и половину дня, подтверждает вслух, даёт один раз переделать выбор", "Спрашивает день и половину дня, подтверждает вслух, даёт один раз переделать выбор",
"Повторяет попытки не чаще чем раз в 2 часа, максимум 5 раз, только 09:00–20:00 в рабочие дни", "Граница половины дня — 13:00: звоним после 13:00, значит говорим про вторую половину",
"Повторяет попытки не чаще чем раз в час, максимум 5 раз, только 09:00–20:00 в рабочие дни",
"После двух «взял, но не нажал» или исчерпания попыток передаёт заказ менеджеру", "После двух «взял, но не нажал» или исчерпания попыток передаёт заказ менеджеру",
"Отказ (цифра 9) останавливает обзвон клиента навсегда",
"Каждая попытка и результат видны в карточке заказа; ручной звонок идёт клиенту и показывает своё состояние", "Каждая попытка и результат видны в карточке заказа; ручной звонок идёт клиенту и показывает своё состояние",
]}, ]},
], ],

View File

@ -17,7 +17,6 @@ const OUTCOMES = new Set([
"no_input_day", "no_input_day",
"no_input_half", "no_input_half",
"operator", "operator",
"opt_out",
"invalid_number", "invalid_number",
"error", "error",
]); ]);
@ -196,7 +195,7 @@ Deno.serve(async (request) => {
await supabase.from("order_groups").update(update).eq("id", orderGroupId); await supabase.from("order_groups").update(update).eq("id", orderGroupId);
} }
// ── Эскалация ── // ── Эскалация ──
else if (["operator", "opt_out", "invalid_number"].includes(body.outcome)) { else if (["operator", "invalid_number"].includes(body.outcome)) {
const update: Record<string, unknown> = { const update: Record<string, unknown> = {
notification_status: "manual_required", notification_status: "manual_required",
called: true, called: true,
@ -204,11 +203,8 @@ Deno.serve(async (request) => {
called_comment: called_comment:
body.outcome === "operator" body.outcome === "operator"
? "Просил менеджера" ? "Просил менеджера"
: body.outcome === "opt_out" : "Неверный номер",
? "Отказался от автозвонков"
: "Неверный номер",
}; };
if (body.outcome === "opt_out") update.autocall_opt_out = true;
await supabase.from("order_groups").update(update).eq("id", orderGroupId); await supabase.from("order_groups").update(update).eq("id", orderGroupId);
} }
// ── no_answer / no_input: решаем, эскалировать ли ── // ── no_answer / no_input: решаем, эскалировать ли ──