Заказы
Бизнес-спека
Routes
| Route | Страница |
|---|---|
/orders/active | Активные заказы |
/orders/history | История заказов |
/orders/:id | Карточка заказа |
Sidebar
Группа “Заказы” в навигации (после “Склад”), видна всем ролям кроме Кассира.
Пункты:
- Активные (
/orders/active) - История (
/orders/history)
1. Активные заказы (ActiveOrdersPage)
Layout
- Header: “Активные заказы”
- Store selector — дропдаун выбора ТТ (как на стоп-листах)
- Таблица активных заказов
- Auto-refresh каждые 30 секунд
Store selector
| Роль | Поведение |
|---|---|
| Франшиза | Дропдаун со всеми ТТ |
| Франчайзи | Дропдаун только с собственными ТТ |
| Менеджер ТТ | Автовыбор единственной ТТ, дропдаун disabled |
API: GET /api/v1/admin/stores → выбор ТТ → GET /api/v1/admin/orders?store_id=...&status=new,accepted,ready,handed_over,in_delivery,delivered
Активные статусы после BR 2.5 — все кроме closed и cancelled.
Таблица
| Колонка | Данные | Формат |
|---|---|---|
| № | order_number | ”001” |
| Время | created_at | Относительное: “5 мин назад”, “1 ч назад” |
| Тип | order_type | Навынос / Доставка / В зале (dine_in/takeaway/delivery) |
| Канал | channel + external_provider | Касса / Я.Еда / Маркет Деливери |
| Статус | status | Badge: new=серый «Новый», in_progress=жёлтый «В работе», accepted=синий «Готовится», ready=зелёный «Готов», handed_over=голубой «У курьера», in_delivery=синий «В пути», delivered=фиолетовый «Доставлен», rejected=красный (BR 2.5 — добавлены in_delivery, delivered) |
| Сумма | total | ”630 ₽“ |
| Оплата | paid_at | Badge: paid=зелёный “Оплачен”, unpaid=серый “Не оплачен” |
| Кассир | created_by | Имя сотрудника (lookup) |
| Курьер | courier_id | Имя сотрудника-курьера (lookup). Показывается только для order_type=delivery (BR 2.5) |
Действия
- Клик по строке → переход на
/orders/:id(OrderDetailPage) - Для агрегаторских (
channel=aggregator) заказов на карточке доступен flow-экшены: Принять (/accept→accepted), Готов (/ready→ready), Отдать (/hand-over→handed_over), Отклонить (/reject→rejected).
Состояния
- Нет выбранной ТТ: “Выберите торговую точку”
- Нет данных: “Нет активных заказов”
- Загрузка: skeleton-строки
Auto-refresh
- Таймер 30 секунд — повторный запрос списка
- Индикатор: “Обновлено: HH:mm:ss” в правом верхнем углу
2. История заказов (OrderHistoryPage)
Layout
- Header: “История заказов”
- Store selector — дропдаун выбора ТТ (аналогично ActiveOrdersPage)
- Фильтры: диапазон дат, статус
- Таблица заказов
- Пагинация
Фильтры
| Фильтр | Тип | Описание |
|---|---|---|
| ТТ | Dropdown | Выбор торговой точки (по роли) |
| Дата от | Date picker | date_from |
| Дата до | Date picker | date_to |
| Статус | Dropdown | closed, cancelled, все |
API: GET /api/v1/admin/orders?store_id=...&date_from=...&date_to=...&status=...&page=...&per_page=20
Таблица
| Колонка | Данные | Формат |
|---|---|---|
| № | order_number | ”001” |
| Дата | created_at | ”06.04.2026 14:30” |
| Тип | order_type | ”Навынос” |
| Статус | status | Badge: closed=синий «Закрыт», cancelled=красный «Отменён». Для promoting legacy показываются также finalized-состояния delivery (delivered для отфильтрованной истории доставки) (BR 2.5) |
| Сумма | total | ”630 ₽“ |
| Оплата | payment_method | Badge: cash=серый “Наличные”, card=синий “Карта”, qr=фиолетовый “QR”, mixed=оранжевый “Смешанная” |
| Кассир | created_by | Имя сотрудника |
Действия
- Клик по строке → переход на
/orders/:id(OrderDetailPage)
Пагинация
- 20 записей на страницу
- Кнопки: ”< Назад”, “Далее >”, номер текущей страницы
Состояния
- Нет выбранной ТТ: “Выберите торговую точку”
- Нет данных: “Нет заказов за выбранный период”
- Загрузка: skeleton-строки
3. Карточка заказа (OrderDetailPage)
Layout
- Breadcrumb: Заказы > Активные/История > #001
- Header: номер заказа, статус badge, название ТТ, дата, кассир
- Блок «Клиент» (если прикреплён) (Добавлено в BR 3.1)
- Таблица позиций
- Итого
- Секция оплаты (если оплачен)
Блок «Клиент»
(Добавлено в BR 3.1)
Показывается если customer_id != null. Слева от блока позиций.
| Поле | Данные | Формат |
|---|---|---|
| Имя | customer.first_name + last_name | Кликабельное — переход в /customers/{customer_id} (карточка клиента) |
| Телефон | customer.phone | +7 (999) 123-45-67 |
| Группы | customer.groups[].name | Chips (max 3 видимых) |
Если customer_id == null — блок не отображается (заказ анонимный).
Header
| Поле | Данные | Формат |
|---|---|---|
| Номер | order_number | ”Заказ #001” |
| Статус | status | Badge (цвет по статусу) |
| ТТ | store name | Название ТТ (lookup) |
| Дата | created_at | ”06.04.2026 14:30” |
| Кассир | created_by | Имя сотрудника |
| Тип | order_type | ”Навынос” |
Таблица позиций
| Колонка | Данные | Формат |
|---|---|---|
| Товар | product_name | ”Шаурма классическая” |
| Кол-во | quantity | ”2” |
| Цена | unit_price | ”250 ₽“ |
| Модификаторы | modifiers | ”Соус: Чесночный (+30 ₽)” — каждый модификатор с новой строки |
| Стоимость | total_price | ”500 ₽“ |
| Комментарий | notes | ”без лука” (курсив, мелкий шрифт) |
Итого
- Итого:
total— жирный, справа внизу таблицы
Секция оплаты
Показывается если paid_at != null.
| Поле | Данные | Формат |
|---|---|---|
| Способ оплаты | payment_method | ”Наличные” / “Карта” / “QR” / “Смешанная” |
| Сумма | paid_amount | ”630 ₽“ |
| RRN | rrn | Показывается если есть (для карты/QR) |
| Карта | card_last4 | ”**** 1234” (если есть) |
| Оплачен | paid_at | ”06.04.2026 14:35” |
Секция отмены
Показывается если status == cancelled.
| Поле | Данные | Формат |
|---|---|---|
| Причина отмены | cancel_reason | Текст |
| Отменён | cancelled_at | ”06.04.2026 14:40” |
Секция возвратов (BR 3.3)
(Добавлено в BR 3.3)
Показывается если у заказа есть хотя бы один RefundRecord. Список возвратов с полями:
| Поле | Данные | Формат |
|---|---|---|
| Сумма | amount | ”500 ₽“ |
| Статус | status | Badge: started серый «В процессе», done зелёный «Выполнен», failed красный «Ошибка» |
| Причина | reason | Текст |
| Инициирован | initiated_by, created_at, cashier_id → имя | ”23.04.2026 15:00, Иванов И.И.” |
Ошибка (если failed) | error_message | Текст от PK |
Для статуса started — автообновление полем раз в 10 сек (через poll GET /api/v1/admin/orders/:id) пока не перейдёт в финальный (done/failed).
Кнопка «Вернуть» (админский возврат через PK)
Видна в шапке карточки при:
order.statusIN (closed,delivered) или (ready/handed_over/in_deliveryсpaid_at != null)- Permission
orders.refund orders.pk_payment_id != null(еслиnull— legacy-возврат через старый flow без PK)
Клик → модалка:
- Radio: «Полный возврат» / «Частичный возврат»
- Если частичный — список позиций с чекбоксами и количеством (max = оригинал)
- Поле «Причина» (textarea, обязательно)
- Итого к возврату (автосумма)
- Кнопка «Вернуть»
API: POST /api/v1/admin/orders/:id/refund → Order Service публикует order.refund_requested → Paykeeper Adapter вызывает PK → через N минут PK шлёт webhook → статус переходит в done/failed.
Детали flow — Интеграция PayKeeper.
Секция фискальных данных (BR 3.3)
Показывается если у заказа fiscal_data != null (заполняется после paykeeper.receipt.fiscalized).
| Поле | Данные | Формат |
|---|---|---|
| Фискальный признак (ФПД) | fiscal_data.fpd | Моноширинный |
| Номер ФД | fiscal_data.fnd | |
| Номер ФН | fiscal_data.fn | |
| РН ККТ | fiscal_data.rnkkt | |
| Номер смены | fiscal_data.shift_number | |
| Номер чека | fiscal_data.receipt_number | |
| Чек | pk_fop_receipt_key | Ссылка «Открыть чек» → URL формата https://{pk_host}/receipt/{pk_payment_id}/{fop_receipt_key} |
Если fiscal_failed=true — красный бейдж «Фискализация не удалась» + ссылка на подробности в журнале PK.
Режим
Read-only
Большинство данных заказа в админке — только просмотр. Единственные действия из админки: возврат (через PK, см. выше) и просмотр фискальных данных. Управление статусами заказа происходит через POS.
API: GET /api/v1/admin/orders/:id, POST /api/v1/admin/orders/:id/refund
Ролевой доступ
| Роль | Доступ |
|---|---|
| Франшиза | Просмотр заказов всех ТТ |
| Франчайзи | Просмотр заказов своих ТТ |
| Менеджер ТТ | Просмотр заказов своей ТТ |
| Кассир | Нет доступа (раздел скрыт в админке) |