# Поток n8n для `order_groups` ## Цель `Supabase` хранит состояние доставки, генерирует ссылку для клиента и сохраняет все временные отметки. `n8n` отвечает за отправку SMS, повторные попытки и перевод зависших групп в ручную обработку. Короткая ссылка с нашей стороны не нужна. Мы храним полную `delivery_link`, а сервис SMS сам сокращает ссылку при отправке. ## Источник истины Основная запись находится в `public.order_groups`. Важные поля: - `status` - бизнес-готовность группы - `delivery_status` - состояние согласования доставки для клиента и логиста - `delivery_link` - полная публичная ссылка на `/delivery/:token` - `delivery_invitation_id` - связанная запись приглашения - `notification_status` - состояние SMS-оркестрации для `n8n` - `sms_attempts` - сколько было попыток отправки SMS - `first_sms_sent_at` - время первой SMS - `second_sms_sent_at` - время второй SMS - `last_sms_error` - текст последней ошибки провайдера - `next_notification_check_at` - когда `n8n` должен вернуться к записи - `delivery_date` и `delivery_time` - выбранный слот после подтверждения клиентом ## Окно отправки SMS Чтобы не тревожить клиентов ночью, SMS отправляются только в местное окно: ```text 09:00-20:00 Europe/Simferopol ``` Если запись стала готова к отправке вне этого окна, SMS не отправляется сразу. `Supabase` должен поставить ближайшее разрешенное время в `next_notification_check_at`: - до `09:00` - сегодня в `09:00` - с `09:00` до `20:00` - сразу - после `20:00` - завтра в `09:00` Для этого в SQL есть helper: ```sql public.next_order_group_sms_check_at(start_from timestamptz, delay interval) ``` `n8n` не должен сам решать, можно ли сейчас тревожить клиента. Он просто выбирает записи, где `next_notification_check_at <= now()`. ## Рекомендуемая модель статусов ### `delivery_status` - `pending_confirmation` - клиент еще не выбрал слот - `agreed` - клиент выбрал слот доставки - `manual_confirmation_required` - автоматический поток не сработал, менеджер/логист должен продолжить вручную - `no_contact` - менеджер/логист пытался связаться вручную, но связи с клиентом нет - `assigned_to_driver` - доставка согласована и передана в планирование водителю - `out_for_delivery` - водитель уже в работе - `delivered` - доставка завершена - `cancelled` - группу больше не нужно обрабатывать ### `notification_status` - `not_started` - ссылка еще не подготовлена - `link_ready` - `Supabase` создал `delivery_link`, `n8n` может отправлять первую SMS - `first_sms_sent` - первая SMS принята провайдером - `second_sms_sent` - отправлено напоминание - `confirmed` - клиент выбрал слот - `manual_required` - после повторов подтверждения нет - `send_failed` - ошибка провайдера/API, можно повторить ## Ответственность Supabase ### 1. Подготовка ссылки Когда `order_group` попадает в поток согласования доставки: - `status = 'ready_for_notification'` - `delivery_status = 'pending_confirmation'` Триггер Supabase `order_groups_ensure_delivery_link` должен: - создать запись `delivery_invitations` с `order_group_id` - сгенерировать token и полную публичную ссылку - подготовить слоты только на завтра и послезавтра - записать `delivery_link` в `order_groups` - установить `delivery_invitation_id` - установить `notification_status = 'link_ready'` - установить `next_notification_check_at` через `public.next_order_group_sms_check_at()` SQL для этого триггера лежит здесь: ```text docs/sql/order-groups-auto-delivery-link.sql ``` `n8n` больше не должен вызывать `create-delivery-invitation` для `order_groups`. Ему нужно ждать, пока в строке уже будут `notification_status = 'link_ready'` и `delivery_link is not null`. ### 2. Прием выбора клиента Публичная страница использует token. Когда клиент подтверждает слот, `confirm-delivery-choice` должен: - сохранить `delivery_date` и `delivery_time` - установить `delivery_status = 'agreed'` - установить `notification_status = 'confirmed'` Это становится стоп-сигналом для всех напоминаний в `n8n`. ## Потоки n8n ### Поток 1. Отправка первой SMS Триггер: - Cron каждые 5-10 минут - Опционально запасной webhook-триггер, если потом захочешь push-старт Запрос: - `status = 'ready_for_notification'` - `delivery_status = 'pending_confirmation'` - `notification_status = 'link_ready'` - `delivery_link is not null` - `next_notification_check_at <= now()` Действие: - отправить SMS с `delivery_link` При успехе обновить `order_groups`: - `notification_status = 'first_sms_sent'` - `sms_attempts = 1` - `first_sms_sent_at = now()` - `sms_sent_at = now()` - `last_sms_error = null` - `next_notification_check_at = public.next_order_group_sms_check_at(now(), interval '3 hours')` Важно: если первая SMS ушла, например, в `18:30`, проверка через 3 часа попала бы на `21:30`. Helper перенесет следующую проверку на завтра `09:00`. При ошибке обновить `order_groups`: - `notification_status = 'send_failed'` - `last_sms_error = <ошибка провайдера>` - `next_notification_check_at = public.next_order_group_sms_check_at(now(), interval '10 minutes')` ### Поток 2. Наблюдатель доставки Триггер: - Cron каждые 10 минут Назначение: - найти записи, где первый поток не завершился корректно - повторить неудачные первые отправки Кандидаты для выбора: - `notification_status = 'send_failed'` - `delivery_status = 'pending_confirmation'` - `next_notification_check_at <= now()` Поведение: - повторить первую SMS - если успех, перевести в `first_sms_sent` - если повторные ошибки превысили выбранный порог, перевести в `manual_required` - все новые значения `next_notification_check_at` считать через `public.next_order_group_sms_check_at(...)` ### Поток 3. Напоминание по SMS Триггер: - Cron каждые 10 минут Запрос: - `delivery_status = 'pending_confirmation'` - `notification_status = 'first_sms_sent'` - `next_notification_check_at <= now()` Действие: - отправить вторую SMS-напоминалку с той же `delivery_link` При успехе обновить: - `notification_status = 'second_sms_sent'` - `sms_attempts = 2` - `second_sms_sent_at = now()` - `last_sms_error = null` - `next_notification_check_at = public.next_order_group_sms_check_at(now(), interval '3 hours')` При ошибке обновить: - `notification_status = 'send_failed'` - `last_sms_error = <ошибка провайдера>` - `next_notification_check_at = public.next_order_group_sms_check_at(now(), interval '30 minutes')` ### Поток 4. Передача в ручную обработку Триггер: - Cron каждые 10 минут Запрос: - `delivery_status = 'pending_confirmation'` - `notification_status = 'second_sms_sent'` - `next_notification_check_at <= now()` Действие: - остановить автоматические напоминания - перевести группу в ручную обработку Обновление: - `delivery_status = 'manual_confirmation_required'` - `notification_status = 'manual_required'` После этого автоматические SMS больше не отправляются. В ЛК менеджер/логист должен вручную выбрать дату доставки или поставить результат `no_contact`, если связаться с клиентом не удалось. ### Поток 5. Условия остановки Каждый поток должен игнорировать строки, где: - `delivery_status in ('agreed', 'no_contact', 'assigned_to_driver', 'out_for_delivery', 'delivered', 'cancelled')` - `notification_status in ('confirmed', 'manual_required')` Это не дает слать дублирующие SMS после ответа клиента или передачи кейса человеку. ## Рекомендуемый текст SMS Пример: ```text Ваш заказ готов к согласованию доставки. Выберите удобные дату и время по ссылке: {{delivery_link}} ``` Напоминание: ```text Напоминаем: нужно выбрать дату и время доставки вашего заказа. Ссылка: {{delivery_link}} ``` ## Что нужно фронтенду Публичной странице нужны только: - token из URL - `get-delivery-invitation` - `confirm-delivery-choice` Никакой логики SMS на фронтенде быть не должно. Никакой генерации ссылок на фронтенде быть не должно. ## Минимальный порядок внедрения 1. Развернуть обновленную схему `Supabase` и `docs/sql/order-groups-auto-delivery-link.sql`. 2. Проверить, что insert/update в `order_groups` пишет `delivery_link` и `notification_status = 'link_ready'`. 3. Собрать поток `n8n` для первой SMS. 4. Собрать поток `n8n` для напоминаний. 5. Собрать поток `n8n` для ручной передачи. 6. Проверить полный цикл на одной реальной записи `order_group`. ## Сценарий проверки 1. Пометить одну `order_group` как готовую к клиентскому согласованию доставки. 2. Убедиться, что `delivery_link` появился в `order_groups` автоматически. 3. Дать `n8n` отправить первую SMS. 4. Открыть ссылку и подтвердить слот на клиентской странице. 5. Убедиться, что `delivery_status = 'agreed'` и `notification_status = 'confirmed'`. 6. Убедиться, что потоки напоминаний больше не трогают эту группу.