supersam/docs/n8n-order-group-delivery-fl...

306 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Поток 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` - выбранный слот доставки после подтверждения клиентом
- `delivery_type` - тип получения: `delivery` (доставка) или `pickup` (самовывоз)
- `pickup_date` и `pickup_time_slot` - выбранный слот самовывоза после подтверждения клиентом
## Окно отправки 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` - группу больше не нужно обрабатывать
- `pickup` - клиент выбрал самовывоз
### `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. `confirm-delivery-choice` получает `delivery_type = 'pickup'` вместе с `pickup_date` и `pickup_time_slot`.
2. RPC `confirm_delivery_choice_by_token` устанавливает `delivery_status = 'pickup'`, `delivery_type = 'pickup'`, `pickup_date`, `pickup_time_slot`.
3. SMS-потоки n8n должны игнорировать строки со `delivery_status = 'pickup'` — клиент сам забирает заказ, напоминания о доставке не нужны.
## Минимальный порядок внедрения
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. Убедиться, что потоки напоминаний больше не трогают эту группу.