docs: update documentation for pickup (самовывоз) feature and autodeploy
This commit is contained in:
parent
3651dbb484
commit
14fe89f899
27
README.md
27
README.md
|
|
@ -9,9 +9,26 @@ npm install
|
|||
npm run dev
|
||||
```
|
||||
|
||||
## Деплой
|
||||
|
||||
Приложение разворачивается через Docker (`docker-compose.app.yml`). Сборка и запуск:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.app.yml up -d --build
|
||||
```
|
||||
|
||||
### Автодеплой
|
||||
|
||||
Настроен webhook в Gitea: при пуше в `main` автоматически запускается деплой.
|
||||
|
||||
- **Webhook listener**: systemd-сервис `supersam-webhook` на порту 9000
|
||||
- **Gitea hook**: push в `main` → `POST http://10.0.2.1:9000/webhook/supersam`
|
||||
- **deploy.sh**: `git pull origin main` → `docker compose up -d --build`
|
||||
- Репозиторий: `https://git.supersamsev.ru/mihail/supersam`
|
||||
|
||||
## Главный документ
|
||||
|
||||
- [Обзор системы](/Users/mihailkucer/Documents/super-sam/docs/product-overview.md) — назначение приложения, роли, сценарии, клиентский flow и подготовка к показу.
|
||||
- [Обзор системы](docs/product-overview.md) — назначение приложения, роли, сценарии, клиентский flow и подготовка к показу.
|
||||
|
||||
## Что уже есть
|
||||
|
||||
|
|
@ -19,15 +36,23 @@ npm run dev
|
|||
- Role-based dashboard для менеджера, логиста и водителя.
|
||||
- Карточка заказа с составом, комментариями и историей.
|
||||
- Публичная страница `/delivery/:token` для выбора даты, половины дня и просмотра состава заказа.
|
||||
- **Самовывоз** — вкладки Доставка/Самовывоз на клиентской странице, выбор даты и половины дня самовывоза, информация о бесплатном хранении (2 рабочих дня) и платном (300₽/день).
|
||||
- Supabase SQL-схема, таблицы приглашений и Edge Functions для invitation flow.
|
||||
- Документация по продукту, архитектуре и сценариям.
|
||||
|
||||
## Структура
|
||||
|
||||
- `src/` — интерфейс и клиентская логика.
|
||||
- `src/constants/deliveryWorkflow.js` — статусы доставки, включая самовывоз.
|
||||
- `src/components/client/DeliverySlotsPicker.jsx` — виджет выбора слотов доставки.
|
||||
- `src/components/client/PickupSlotsPicker.jsx` — виджет выбора слотов самовывоза.
|
||||
- `src/components/client/DeliveryChoiceFlow.jsx` — флоу согласования доставки/самовывоза.
|
||||
- `src/pages/ClientDeliveryPage.jsx` — публичная страница с вкладками Доставка/Самовывоз.
|
||||
- `src/components/orders/OrderDetailPanel.jsx` — карточка заказа с управлением доставкой и самовывозом.
|
||||
- `supabase/schema.sql` — структура БД, роли, индексы, RLS, триггеры.
|
||||
- `supabase/functions/` — Edge Functions для приглашений, статусов и чат-коммуникаций.
|
||||
- `supabase/seed/stage-1-demo.sql` — набор seed-данных для показа заказчику.
|
||||
- `docs/architecture.md` — архитектура фронтенда и модулей.
|
||||
- `docs/product-overview.md` — общий обзор продукта, ролей и сценариев.
|
||||
- `docs/scenarios.md` — сценарии жизненного цикла заказа.
|
||||
- `docs/n8n-order-group-delivery-flow.md` — потоки n8n для оркестрации доставки.
|
||||
|
|
|
|||
28
deploy.sh
28
deploy.sh
|
|
@ -1,30 +1,6 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
cd /opt/supersam
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploy starting..."
|
||||
|
||||
# Pull latest from main
|
||||
git fetch origin main
|
||||
BEFORE=$(git rev-parse HEAD)
|
||||
git reset --hard origin/main
|
||||
AFTER=$(git rev-parse HEAD)
|
||||
|
||||
if [ "$BEFORE" = "$AFTER" ]; then
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] No changes, skipping rebuild."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Updated: ${BEFORE:0:7} -> ${AFTER:0:7}"
|
||||
|
||||
# Rebuild and restart
|
||||
git pull origin main
|
||||
docker compose -f docker-compose.app.yml up -d --build
|
||||
|
||||
# Wait for container to be healthy
|
||||
sleep 3
|
||||
if docker ps --format '{{.Names}}' | grep -q 'supersam-app'; then
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploy complete. Container running."
|
||||
else
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Container not running after deploy!"
|
||||
exit 1
|
||||
fi
|
||||
echo 'Deploy completed at' $(date)
|
||||
|
|
|
|||
|
|
@ -6,17 +6,25 @@
|
|||
- `src/context/ThemeContext.jsx` — управление светлой и тёмной темой через `data-theme`.
|
||||
- `src/hooks/usePwaStatus.js` — клиентское состояние PWA: online/offline, install prompt, standalone и offline readiness.
|
||||
- `src/hooks/useOrders.js` — локальный state заказов, истории, чатов, фильтров, действий и **сгруппированных наборов доставки** (deliverySetBuckets).
|
||||
- `src/hooks/useOrderGroups.js` — работа с группами заказов: загрузка, обновление статусов, ручное согласование доставки и самовывоза. Метод `saveManualDeliveryChoice` принимает `deliveryType`, `pickupDate`, `pickupTimeSlot`.
|
||||
- `src/services/deliverySetViews.js` — чистые функции группировки импортированных заказов в наборы доставки с buckets: «На подходе», «Готово к запуску», «Ожидает клиента», «Нужна ручная работа», «Согласовано», «Завершено».
|
||||
- `src/services/orderService.js` — чистые функции бизнес-логики заказов, покрытые тестами.
|
||||
- `src/services/supabase/orderRepository.js` — адаптер реальных чтений/записей заказов и чатов в Supabase, включая source-поля 1С и delivery-set поля.
|
||||
- `src/services/supabase/orderGroupRepository.js` — репозиторий групп заказов. Маппинг полей включает `deliveryType`, `pickupDate`, `pickupTimeSlot`. Метод `updateOrderGroupDeliveryChoice` поддерживает как доставку, так и самовывоз.
|
||||
- `src/services/deliveryWorkflow.js` — карта статусов доставки и переходов. Включает статус `pickup` (Самовывоз) с переходами в/из других статусов.
|
||||
- `src/services/orderGroupViews.js` — метки и цвета для статусов, включая `pickup: "Самовывоз"`.
|
||||
- `src/services/deliveryInvitationApi.js` — API для приглашений доставки. `confirmDeliveryChoice` принимает `deliveryType`, `pickupDate`, `pickupTimeSlot`.
|
||||
- `src/services/driverDeliveries.js` — фильтрация и группировка доставок для рабочей области водителя.
|
||||
- `src/layouts/AppShell.jsx` — общий shell с боковой навигацией, уведомлениями и переключением темы.
|
||||
- `src/components/logistics/LogisticsReadinessBoard.jsx` — интерактивная доска с bucket-ами наборов доставки.
|
||||
- `src/components/logistics/DeliverySetDetailPanel.jsx` — детальная карточка набора доставки: source-поля 1С, production-шаги, слоты, действия.
|
||||
- `src/components/client/DeliverySlotsPicker.jsx` — публичный виджет выбора даты и половины дня доставки.
|
||||
- `src/components/client/DeliveryChoiceFlow.jsx` — публичный поток согласования доставки по приглашению.
|
||||
- `src/components/client/PickupSlotsPicker.jsx` — публичный виджет выбора даты и половины дня самовывоза. Отображает доступные даты (сегодня до 12:00, завтра, послезавтра, без выходных), слоты «До обеда»/«После обеда», и информационный блок о стоимости хранения: «Бесплатное хранение — 2 рабочих дня. С 3-го рабочего дня — 300₽/день.».
|
||||
- `src/components/client/DeliveryChoiceFlow.jsx` — публичный поток согласования доставки или самовывоза по приглашению. Принимает `deliveryType` и показывает динамические лейблы.
|
||||
- `src/components/client/DeliveryStateNotice.jsx` — информационный экран для clients со статусом ссылки.
|
||||
- `src/pages/ClientDeliveryPage.jsx` — публичная страница согласования доставки с вкладками 🚚 Доставка / 🏪 Самовывоз. Переключение типа получения, отображение соответствующего пикера слотов.
|
||||
- `src/components/orders/*` — фильтры, список заказов, карточка заказа, история статусов и поиск по чату.
|
||||
- `src/components/orders/OrderDetailPanel.jsx` — карточка заказа с управлением доставкой и самовывозом. Вкладки Доставка/Самовывоз, поля даты самовывоза и половины дня, кнопка статуса «Самовывоз».
|
||||
- `src/components/orders/OrderEditorPanel.jsx` — создание и редактирование заказа менеджером или администратором.
|
||||
- `src/components/dashboard/ProductionQueuePanel.jsx` — отдельный блок производственной очереди.
|
||||
- `src/components/dashboard/RoleWorkspacePanel.jsx` — рабочая панель с delivery-set bucket-ами для логиста.
|
||||
|
|
@ -31,15 +39,42 @@
|
|||
- **Логист** видит наборы доставки, слоты, сообщения чатбота и ручную обработку исключений.
|
||||
- **Водитель** видит только назначенные доставки и может переводить их через статусы `Загружен`, `В пути`, `Доставлен`, `Проблема доставки`.
|
||||
- **Администратор** видит весь массив заказов, доставок и системные логи.
|
||||
- **Менеджер** может назначить тип доставки (доставка/самовывоз), дату и половину дня.
|
||||
- Клиент не является авторизованным пользователем приложения. Клиент использует публичную ссылку приглашения.
|
||||
|
||||
## Ключевые экраны
|
||||
|
||||
- `/login` — email + OTP flow. При отсутствии `VITE_SUPABASE_*` включается demo-режим. Подсказка проверять входящие и спам. Неизвестный email: «Email не найден в системе. Обратитесь к администратору.»
|
||||
- `/dashboard` — role-based control center: для логиста — LogisticsReadinessBoard с наборами доставки, для водителя — план маршрута и быстрые действия.
|
||||
- `/delivery/:token` — публичная страница согласования доставки для клиента.
|
||||
- `/delivery/:token` — публичная страница согласования доставки для клиента с вкладками Доставка/Самовывоз.
|
||||
- `public/manifest.webmanifest` + `public/service-worker.js` — installable PWA-оболочка и базовое кеширование shell для demo offline.
|
||||
|
||||
## Тип получения: Доставка и Самовывоз
|
||||
|
||||
### Переключатель типа
|
||||
|
||||
На клиентской странице (`ClientDeliveryPage`) и в карточке заказа (`OrderDetailPanel`) реализованы вкладки:
|
||||
- **🚚 Доставка** — стандартный флоу с выбором даты и половины дня.
|
||||
- **🏪 Самовывоз** — выбор даты самовывоза (сегодня/завтра/послезавтра, без выходных) и половины дня.
|
||||
|
||||
### Статус «Самовывоз»
|
||||
|
||||
В `deliveryWorkflow.js` добавлен статус `pickup` с переходами:
|
||||
- `pending_confirmation` → `pickup`
|
||||
- `manual_confirmation_required` → `pickup`
|
||||
- `pickup` → `assigned_to_driver`, `delivered`, `cancelled`
|
||||
|
||||
### База данных
|
||||
|
||||
Колонки в `order_groups`:
|
||||
- `delivery_type text DEFAULT 'delivery'` — тип получения
|
||||
- `pickup_date date` — дата самовывоза
|
||||
- `pickup_time_slot text` — «До обеда» / «После обеда»
|
||||
|
||||
RPC `confirm_delivery_choice_by_token` обновлён: при `delivery_type = 'pickup'` устанавливает `delivery_status = 'pickup'`, `pickup_date` и `pickup_time_slot`.
|
||||
|
||||
Edge function `confirm-delivery-choice` передаёт `p_delivery_type`, `p_pickup_date`, `p_pickup_time_slot` в RPC.
|
||||
|
||||
## Источник заказов: 1С → Supabase
|
||||
|
||||
- Заказы импортируются из 1С через XML и сохраняются в `public.orders` с source-полями: `source_order_number`, `source_customer_name`, `source_accept_at`, `source_ship_at` и т.д.
|
||||
|
|
@ -60,3 +95,9 @@
|
|||
- `src/services/safeSupabaseCall.js` стандартизирует обработку ошибок.
|
||||
- Данные UI разложены по сущностям, совпадающим с таблицами Supabase: `orders`, `order_history`, `chat_messages`, `delivery_slots`, `delivery_invitations`.
|
||||
- В `orders` синхронизированы поля `status`, `delivery_agreement_status`, `assigned_driver_id`, а также source-поля 1С и delivery-set данные.
|
||||
|
||||
## Деплой
|
||||
|
||||
- Docker-сборка через `docker-compose.app.yml` (multi-stage: Node.js build → Caddy serve).
|
||||
- Автодеплой: Gitea webhook → systemd `supersam-webhook` → `deploy.sh` (git pull + docker build).
|
||||
- Домен: `https://dost.supersamsev.ru/`
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@
|
|||
- `second_sms_sent_at` - время второй SMS
|
||||
- `last_sms_error` - текст последней ошибки провайдера
|
||||
- `next_notification_check_at` - когда `n8n` должен вернуться к записи
|
||||
- `delivery_date` и `delivery_time` - выбранный слот после подтверждения клиентом
|
||||
- `delivery_date` и `delivery_time` - выбранный слот доставки после подтверждения клиентом
|
||||
- `delivery_type` - тип получения: `delivery` (доставка) или `pickup` (самовывоз)
|
||||
- `pickup_date` и `pickup_time_slot` - выбранный слот самовывоза после подтверждения клиентом
|
||||
|
||||
## Окно отправки SMS
|
||||
|
||||
|
|
@ -66,6 +68,7 @@ public.next_order_group_sms_check_at(start_from timestamptz, delay interval)
|
|||
- `out_for_delivery` - водитель уже в работе
|
||||
- `delivered` - доставка завершена
|
||||
- `cancelled` - группу больше не нужно обрабатывать
|
||||
- `pickup` - клиент выбрал самовывоз
|
||||
|
||||
### `notification_status`
|
||||
|
||||
|
|
@ -275,6 +278,14 @@ docs/sql/order-groups-auto-delivery-link.sql
|
|||
Никакой логики 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`.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
- показать менеджеру единый реестр доставочных заказов с поиском и карточкой заказа;
|
||||
- показать логисту список доставок на сегодня и ближайшие дни с половинами дня;
|
||||
- показать водителю свои доставки, адрес, состав заказа и базовые статусы;
|
||||
- дать клиенту публичную ссылку, по которой он выбирает дату и половину дня доставки;
|
||||
- дать клиенту публичную ссылку, по которой он выбирает дату и половину дня доставки **или самовывоза**;
|
||||
- хранить состояние заказов, приглашений и истории изменений в Supabase.
|
||||
|
||||
## Роли
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
- видит список заказов доставки;
|
||||
- ищет по номеру заказа, клиенту и телефону;
|
||||
- открывает карточку заказа и смотрит состав, комментарии и историю;
|
||||
- может назначить тип доставки (доставка/самовывоз), дату самовывоза и половину дня;
|
||||
- не работает с созданием заказов и внутренними служебными экранами.
|
||||
|
||||
### Логист
|
||||
|
|
@ -24,7 +25,8 @@
|
|||
- видит заказы, готовые к доставке;
|
||||
- смотрит ближайшие даты: сегодня, завтра и послезавтра;
|
||||
- смотрит половину дня и текущий статус доставки;
|
||||
- открывает карточку заказа, чтобы свериться с деталями.
|
||||
- открывает карточку заказа, чтобы свериться с деталями;
|
||||
- может перевести статус заказа в «Самовывоз» и указать дату.
|
||||
|
||||
### Водитель
|
||||
|
||||
|
|
@ -36,9 +38,40 @@
|
|||
|
||||
- получает публичную ссылку вида `/delivery/:token`;
|
||||
- видит номер заказа и состав заказа;
|
||||
- выбирает дату и половину дня: `До обеда` или `После обеда`;
|
||||
- **выбирает тип получения: Доставка или Самовывоз**;
|
||||
- при выборе доставки — выбирает дату и половину дня: `До обеда` или `После обеда`;
|
||||
- при выборе самовывоза — выбирает дату (сегодня/завтра/послезавтра с учётом выходных) и половину дня;
|
||||
- видит информацию о бесплатном хранении (2 рабочих дня) и платном (300₽/день начиная с 3-го рабочего дня);
|
||||
- подтверждает выбор без входа во внутренний кабинет.
|
||||
|
||||
## Самовывоз
|
||||
|
||||
### Клиентский флоу
|
||||
|
||||
1. Клиент открывает ссылку `/delivery/:token`.
|
||||
2. Видит две вкладки: **🚚 Доставка** и **🏪 Самовывоз**.
|
||||
3. На вкладке «Самовывоз» видит:
|
||||
- Доступные даты: сегодня (если до 12:00 текущего дня готовности), завтра, послезавтра (выходные пропускаются).
|
||||
- Две половины дня: «До обеда» и «После обеда».
|
||||
- Информационный блок: «Бесплатное хранение — 2 рабочих дня. С 3-го рабочего дня — 300₽/день.»
|
||||
4. Выбирает дату и половину дня, подтверждает.
|
||||
5. Статус заказа переходит в `pickup`, в БД сохраняются `pickup_date` и `pickup_time_slot`.
|
||||
|
||||
### Управление в карточке заказа
|
||||
|
||||
- Менеджер, логист и администратор могут переключить тип доставки (Доставка ↔ Самовывоз).
|
||||
- При самовывозе доступны поля: дата самовывоза и половина дня.
|
||||
- Статус «Самовывоз» доступен в кнопках смены статуса.
|
||||
|
||||
### База данных
|
||||
|
||||
В таблице `order_groups` добавлены колонки:
|
||||
- `delivery_type text DEFAULT 'delivery'` — тип получения: `delivery` или `pickup`
|
||||
- `pickup_date date` — дата самовывоза
|
||||
- `pickup_time_slot text` — половина дня самовывоза (`До обеда` / `После обеда`)
|
||||
|
||||
Статус `delivery_status` пополнился значением `pickup` — «Самовывоз».
|
||||
|
||||
## Основные сценарии
|
||||
|
||||
### Внутренний сценарий
|
||||
|
|
@ -48,7 +81,7 @@
|
|||
3. Логист отслеживает готовность и ближайшее окно доставки.
|
||||
4. Водитель получает свою доставку и доводит её до результата.
|
||||
|
||||
### Сценарий клиента
|
||||
### Сценарий клиента (доставка)
|
||||
|
||||
Клиентская страница работает по token из таблицы `public.delivery_invitations`.
|
||||
|
||||
|
|
@ -59,21 +92,27 @@
|
|||
Эта ссылка показывает:
|
||||
- заказ `CD-240031`;
|
||||
- состав заказа;
|
||||
- четыре варианта слота;
|
||||
- две даты;
|
||||
- две половины дня: `До обеда` и `После обеда`.
|
||||
- вкладки «Доставка» и «Самовывоз»;
|
||||
- на вкладке «Доставка» — четыре варианта слота, две даты, две половины дня.
|
||||
|
||||
После подтверждения выбора:
|
||||
- invitation переводится в состояние `agreed`;
|
||||
- заказ переводится в `Доставка согласована`;
|
||||
- в `order_history` появляется запись о подтверждении;
|
||||
- в `delivery_slots` фиксируется подтверждённый слот.
|
||||
### Сценарий клиента (самовывоз)
|
||||
|
||||
При выборе вкладки «Самовывоз»:
|
||||
- доступны даты начиная с дня готовности (если до 12:00) или завтра;
|
||||
- две половины дня: «До обеда» и «После обеда»;
|
||||
- информационный блок о стоимости хранения;
|
||||
- подтверждение устанавливает `delivery_type = 'pickup'`, `delivery_status = 'pickup'`.
|
||||
|
||||
## Что хранится в Supabase
|
||||
|
||||
- `public.users` — пользователи и роли;
|
||||
- `public.orders` — заказы и текущие статусы;
|
||||
- `public.order_history` — история изменений;
|
||||
- `public.order_groups` — группы заказов с полями доставки и самовывоза:
|
||||
- `delivery_type` — `delivery` или `pickup`;
|
||||
- `delivery_date`, `delivery_time` — слот доставки;
|
||||
- `pickup_date`, `pickup_time_slot` — слот самовывоза;
|
||||
- `delivery_status` — статус согласования (включая `pickup`);
|
||||
- `public.delivery_slots` — возможные и подтверждённые слоты доставки;
|
||||
- `public.delivery_invitations` — публичные invitation token и состояние клиентского flow;
|
||||
- `public.integration_events` — технические и интеграционные события.
|
||||
|
|
@ -95,11 +134,13 @@
|
|||
- реестр заказов и карточку заказа;
|
||||
- список доставок по датам для логиста;
|
||||
- карточку доставки водителя;
|
||||
- клиентскую ссылку с выбором даты и половины дня.
|
||||
- клиентскую ссылку с выбором типа получения (доставка/самовывоз) и датой;
|
||||
- информационный блок о стоимости хранения при самовывозе.
|
||||
|
||||
## Полезные документы
|
||||
|
||||
- [README](/Users/mihailkucer/Documents/super-sam/README.md)
|
||||
- [Архитектура](/Users/mihailkucer/Documents/super-sam/docs/architecture.md)
|
||||
- [Сценарии](/Users/mihailkucer/Documents/super-sam/docs/scenarios.md)
|
||||
- [Edge Functions](/Users/mihailkucer/Documents/super-sam/supabase/functions/README.md)
|
||||
- [README](../README.md)
|
||||
- [Архитектура](architecture.md)
|
||||
- [Сценарии](scenarios.md)
|
||||
- [Поток n8n](n8n-order-group-delivery-flow.md)
|
||||
- [Edge Functions](../supabase/functions/README.md)
|
||||
|
|
|
|||
|
|
@ -20,15 +20,16 @@
|
|||
- Перечнем заказов набора, их 1С-номерами и шагами производства (раскрой, склейка, криволинейные, контроль качества, отгрузка).
|
||||
- Телефоном и email клиента, городом, связанными счетами.
|
||||
- Текущим статусом слота.
|
||||
3. Логист может запустить приглашение, назначить водителя или перейти к ручной обработке.
|
||||
|
||||
## 3. Согласование доставки с клиентом
|
||||
|
||||
1. Когда набор доставки готов, логист запускает отправку приглашения клиенту.
|
||||
2. Клиент получает ссылку на `/delivery/:token`.
|
||||
3. На странице клиент видит **DeliverySlotsPicker** с доступными датами и половинами дня.
|
||||
4. Клиент выбирает слот и подтверждает. Статус набора переходит в «Ожидает клиента» → «Согласовано».
|
||||
5. Если клиент не отвечает, система или логист переводит набор в «Нужна ручная работа».
|
||||
3. На странице клиент видит вкладки **🚚 Доставка** и **🏪 Самовывоз**.
|
||||
4. На вкладке «Доставка» — **DeliverySlotsPicker** с доступными датами и половинами дня.
|
||||
5. На вкладке «Самовывоз» — **PickupSlotsPicker** с датами (сегодня до 12:00, завтра, послезавтра, без выходных) и половинами дня, а также информационный блок: «Бесплатное хранение — 2 рабочих дня. С 3-го рабочего дня — 300₽/день.»
|
||||
6. Клиент выбирает тип получения, слот и подтверждает. Статус набора переходит в «Ожидает клиента» → «Согласовано» или «Самовывоз».
|
||||
7. Если клиент не отвечает, система или логист переводит набор в «Нужна ручная работа».
|
||||
|
||||
## 4. Перенос доставки
|
||||
|
||||
|
|
@ -55,6 +56,30 @@
|
|||
2. После закрытия всех заказов набора он переходит в «Завершено».
|
||||
3. В истории появляется финальная запись, а чат закрывается для активных действий.
|
||||
|
||||
## 8. Самовывоз
|
||||
|
||||
### Клиентский сценарий
|
||||
|
||||
1. Клиент открывает ссылку `/delivery/:token`.
|
||||
2. Выбирает вкладку **🏪 Самовывоз**.
|
||||
3. Видит доступные даты: сегодня (если до 12:00 текущего дня готовности), завтра, послезавтра (выходные пропускаются).
|
||||
4. Выбирает дату и половину дня: «До обеда» или «После обеда».
|
||||
5. Видит информационный блок: «Бесплатное хранение — 2 рабочих дня. С 3-го рабочего дня — 300₽/день.»
|
||||
6. Подтверждает выбор.
|
||||
7. В БД устанавливаются: `delivery_type = 'pickup'`, `delivery_status = 'pickup'`, `pickup_date`, `pickup_time_slot`.
|
||||
|
||||
### Сценарий менеджера/логиста
|
||||
|
||||
1. В карточке заказа (**OrderDetailPanel**) менеджер видит вкладки Доставка/Самовывоз.
|
||||
2. При выборе «Самовывоз» — поля даты и половины дня для самовывоза.
|
||||
3. Кнопка статуса «Самовывоз» доступна для менеджера, логиста и администратора.
|
||||
4. Менеджер может переключить тип доставки в любой момент до передачи водителю.
|
||||
|
||||
### Статусы самовывоза
|
||||
|
||||
- `pickup` — заказ ожидает самовывоза клиентом.
|
||||
- Переходы: `pending_confirmation` → `pickup`, `manual_confirmation_required` → `pickup`, `pickup` → `assigned_to_driver`, `pickup` → `delivered`, `pickup` → `cancelled`.
|
||||
|
||||
## Сценарий показа заказчику
|
||||
|
||||
1. Зайти под логистом.
|
||||
|
|
@ -65,6 +90,6 @@
|
|||
- Фролова И.Д. — «Нужна ручная работа» (платное хранение).
|
||||
- Орлова Н.С. — «Завершено».
|
||||
3. Кликнуть по набору Савина — увидеть source-поля, production-шаги, готовность к запуску.
|
||||
4. Перейти на публичную страницу приглашения — увидеть `DeliverySlotsPicker` с выбором даты и половины дня.
|
||||
4. Перейти на публичную страницу приглашения — увидеть вкладки «Доставка» и «Самовывоз» с выбором даты и половины дня.
|
||||
5. Зайти под водителем — увидеть назначенные доставки с адресами и быстрыми действиями.
|
||||
6. Зайти под несуществующим email — увидеть «Email не найден в системе. Обратитесь к администратору.»
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Simple webhook listener for Gitea push events to trigger supersam deploy."""
|
||||
import json
|
||||
import hashlib
|
||||
import hmac
|
||||
import subprocess
|
||||
import logging
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
SECRET = '1032ef91aacd726907bb72c023813c6827f56354902cc7b30e72626690c6d51d'
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
||||
logger = logging.getLogger('webhook')
|
||||
|
||||
class WebhookHandler(BaseHTTPRequestHandler):
|
||||
def verify_signature(self, body):
|
||||
signature = self.headers.get('X-Gitea-Signature', '')
|
||||
if not signature:
|
||||
return False
|
||||
computed = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
|
||||
return hmac.compare_digest(signature, computed)
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == '/webhook/supersam':
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
body = self.rfile.read(content_length) if content_length else b''
|
||||
if not self.verify_signature(body):
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"error": "invalid signature"}')
|
||||
return
|
||||
try:
|
||||
payload = json.loads(body) if body else {}
|
||||
ref = payload.get('ref', '')
|
||||
if ref == 'refs/heads/main':
|
||||
logger.info('Push to main detected, starting deploy...')
|
||||
result = subprocess.run(
|
||||
['/opt/supersam/deploy.sh'],
|
||||
capture_output=True, text=True, timeout=600
|
||||
)
|
||||
logger.info('Deploy exit code: %s', result.returncode)
|
||||
if result.returncode != 0:
|
||||
logger.error('Deploy stderr: %s', result.stderr[-2000:])
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'status': 'deployed', 'exit_code': result.returncode}).encode())
|
||||
else:
|
||||
logger.info('Ignoring push to %s', ref)
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"status": "ignored"}')
|
||||
except Exception as e:
|
||||
logger.error('Error: %s', e)
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps({'error': str(e)}).encode())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == '/health':
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"status": "ok"}')
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = HTTPServer(('0.0.0.0', 9000), WebhookHandler)
|
||||
logger.info('Webhook listener on http://0.0.0.0:9000')
|
||||
server.serve_forever()
|
||||
Loading…
Reference in New Issue