BR 2.3 (draft) — Статусная модель заказа: Yuma-паритет
Это research-заметка, не финальная спека
Записано «на подумать» — собрано из исследования YumaPOS и аудита текущего кода Order Service. Возвращаться к ней после обсуждения приоритетов.
1. Контекст
Текущая статусная модель Order Service работает, но устарела относительно спеки (03-Services/Order Service/Data Model.md) и не покрывает реальных кассовых сценариев. Разница с юмой (наш референс) — в степени детализации flow.
Проверено:
- Сейчас все заказы (takeaway / dine_in / delivery) идут по одному и тому же flow:
new → accepted → ready → closed - Быстрая продажа (клиент попросил бутылку воды) вынужденно проходит «в работу» → «готов», хотя по факту готовить нечего
- Нет явной кнопки «Начать готовку» — статус двигается косвенно, внутренними переходами
OrderItemне имеетstatus— кухня не может отмечать позиции по отдельности
2. Что удалось выяснить про Yuma
2.1. Статусы (см. process-orders)
| Русский | Английский в нашей модели | Смысл |
|---|---|---|
| Новый | new | Создан, в работу не ушёл |
| В процессе | in_progress | На кухне/собирается |
| Готов / Выполнен | ready | Все позиции готовы, ждёт выдачи |
| Закрыт | closed | Оплачен + выдан клиенту |
| Отменён | cancelled | Отменён до оплаты |
2.2. Типы заказа определяют flow
| Тип | Flow | Кухня |
|---|---|---|
| Быстрый заказ (quick / counter-sale) | Новый → Закрыт | Нет |
| За столом (dine-in) | Новый → В процессе → Готов → Закрыт | Да, по явной кнопке |
| Доставка (delivery) | Новый → В процессе → Готов → Передан водителю → Доставляется → Доставлено → Закрыт | Да |
| Автокафе (drive-through) | Как dine-in | Да |
| Групповой | Как dine-in | Да |
Ключевое: тип выбирается до добавления товаров, не на чекауте.
2.3. Кухня включается явной кнопкой «Начать»
Для dine-in/delivery переход Новый → В процессе триггерится нажатием официанта/кассира. Не чекаутом. Сам факт оплаты не равен началу готовки.
Если за столом попросили только воду (готовить нечего) — официант может пропустить «Начать», сразу принять оплату, заказ идёт короткой дорогой Новый → Закрыт.
2.4. Item-level статусы
У юмы каждая позиция в заказе имеет собственный статус: В процессе → Готов. Когда все позиции Готов, заказ автоматически → Готов. Кухонный экран (KDS) показывает именно позиции, а не заказ целиком.
2.5. Принтеры/станции на уровне товара
У каждого товара — поле «Принтеры»: на какой кухонный/барный принтер уедет чек. Если у товара нет принтера — он не печатается никуда, но висит в заказе (для официанта). Это и есть механизм «заказ с водой не идёт на кухню».
2.6. Paid ≠ Closed
Юма разделяет оплату и выдачу:
- Оплачено — зелёная иконка у суммы, статус остаётся
Ready - Закрыт — клиент забрал товар, заказ финализирован
- Есть настройка «автозакрытие через N сек после оплаты» per-терминал — опционально
2.7. Delivery substatus-chain
Для доставочных заказов — отдельная цепочка поверх основного статуса:
Ready → Передан водителю (handed_over) → Доставляется (in_delivery) → Доставлено (delivered) → Закрыт
Каждый substatus — отдельное событие в event bus, курьерское приложение меняет их по мере выполнения.
2.8. Refund vs Cancel — строгое разделение
- Cancel — до оплаты. Клик «Отменить» возможен только в
НовыйилиВ процессе(неоплаченный) - Refund — после оплаты. Отдельная сущность, возврат строго тем же способом оплаты (card→card, cash→cash, online→card)
- Permissions разные:
Cancel OrdervsReturn
2.9. Split-payment для dine-in
В dine-in можно разделить оплату по стульям: «этот стул платит картой, этот — наличкой». Сущности seat и payment_line связаны с order.
3. Что у нас сейчас (аудит erp-order-service)
Что есть
- ✅ Основная цепочка
new → accepted → ready → closed/cancelled - ✅
acceptedкак русский аналог «В процессе» (на уровне неймингов — расхождение со спекой, но фактически этоin_progress) - ✅
handed_overдля аггрегаторных заказов — аналог первого шага delivery-substatus - ✅ Поле
order_type(takeaway/dine_in/delivery) - ✅ Поле
channel(INTERNAL/ агрегаторы) - ✅ Поля
paid_atиcompleted_atотдельно — технически позволяют разделить paid и closed - ✅
RefundRecord+RefundRequest— возвраты отдельно от отмены - ✅
refund_total, статусы refund_request (pending/confirmed/rejected/expired) - ✅ Миграция
005-add-aggregator-statuses.xml— расширила CHECK до включенияacceptedиhanded_over
Чего нет
- ❌ Item-level статусов.
OrderItemне имеет поляstatus. Кухня может только двигать весь заказ целиком. - ❌ Зависимости flow от типа заказа.
takeawayпроходит все статусы, как иdine_in. Быстрая продажа засоряет кухонный queue. - ❌ Аналога «Начать» как явного жеста. Переходы
new → acceptedделаются внутренней логикой, не кнопкой кассира. - ❌ Полной delivery substatus-chain.
handed_overесть, ноin_delivery/deliveredнет. - ❌ Paid ≠ Closed разделения. POS-checkout склеивает оплату и закрытие в одном вызове.
- ❌ Автозакрытия по таймеру.
- ❌ Split payment по стульям. Dine-in работает, но оплата одна на весь заказ. Нет
seat_id/payment_line. - ❌ Кухонных станций / принтеров. У
Productнет поля типаkitchen_station_id/requires_kitchen. Нельзя сказать «этот товар на кухню не идёт». - ❌ Actual KDS-экрана с item-level отметками. В
erp-pos/mobileестьKitchenQueueScreen, но он показывает заказы целиком без детализации по позициям.
Расхождение кода со спекой
- Спека
Data Model.mdстрока 88 описывает статусы какnew / in_progress / ready / closed / cancelled - Код пишет
new / accepted / ready / handed_over / closed / cancelled - Миграция 005 легализовала фактическое положение (CHECK allows both)
- Переименовывать
accepted → in_progressсейчас рискованно: 5+ мест в erp-admin/web (STATUS_LABEL, filters), 5+ мест в erp-pos/mobile (KitchenQueueScreen, AggregatorOrdersScreen), типы TS, живые данные в БД на test-VPS - Рекомендация (безопасная): актуализировать спеку под код — добавить
acceptedиhanded_overкак валидные статусы, описать контекст (aggregator flow)
4. Гипотезы что делать (варианты)
Вариант А: минимальная актуализация
Только обновить спеку Data Model.md под текущий код. Без изменения логики. Закрывает документационный долг, но не добавляет фич.
Вариант Б: разделить flow по типу заказа
Добавить правило: takeaway без кухонных позиций → автосокращение до new → closed. dine_in / delivery — полный flow с явным «Начать».
Потребует:
- Поле
Product.requires_kitchen(catalog-service) - Правило в
OrderService.checkout - Кнопка «Начать готовку» в POS-mobile
Вариант В: полный Yuma-паритет
Всё из Б + item-level статусы + delivery substatus-chain + split-payment + paid≠closed + автозакрытие. Это серия отдельных BR, каждая — заметная работа с участием POS-фронта и админки.
5. Открытые вопросы
- Нужно ли split-payment по стульям в MVP? Большинство российских кофеен и фастфуда не делят чек по стульям. Может быть out of scope Phase 1.
- Кухонные станции / принтеры — это поле на
Productили отдельная сущность «кухонная зона»? Юма использует просто список принтеров, привязанных к товару. - Paid ≠ Closed — нужен ли нам визуально «оплачен, но не выдан»? В небольших кофейнях это малоприменимо (клиент оплачивает и сразу получает), но в ресторане за столом — да.
- KDS как отдельное приложение или интеграция в существующий POS-mobile? У юмы — отдельное приложение для кухни.
- Автозакрытие таймером — per-терминал настройка или глобальная? Юма делает per-терминал, это значит нужно поле в
storesилиpos_terminals.
6. Следующие шаги (когда вернёмся)
- Решить скоуп: A / Б / В или их комбинация
- Если Б/В — декомпозировать на отдельные BR (по одной фиче: item-статусы, delivery-chain, split-payment, paid≠closed)
- Обновить
Data Model.mdOrder Service (безопасная часть) - Добавить
Product.requires_kitchenв BR каталога (catalog-service) — предусловие для разделения flow