Кухонный экран

Источники требований

Референс

KDS — отдельное Android-приложение (Tauri 2 + React + SQLite), которое ставится на планшет на кухне или баре. Заменяет бумажные кухонные чеки: повар видит «свои» заказы (по выбранным станциям), отмечает готовность позиций, нажимает «Готово» по станции — заказ уезжает с экрана и кассир получает сигнал «можно подавать».

В P0 транспорт — облако: устройство стучится в pos-bff через интернет. LAN-режим (через POS Desktop как hub) — отложен в BR 5.2; полный offline — в BR 5.3.


§1. Сущности

KDS Device

Устройство-планшет, регистрируется при первом запуске. Хранится в kds_devices (User Service).

ПолеОбязательностьОписание
idАвтоUUID. Генерируется на устройстве при первом запуске, сохраняется в SQLite. Передаётся в каждом запросе как X-Device-Id.
franchise_idОбязательноИз JWT первого логина
store_idОбязательноТТ, к которой привязано устройство. Выбирается при регистрации — далее фиксирована до явной смены
nameОпциональноПонятное имя — «Планшет горячего цеха», «Бар-1» (задаётся в админке)
app_versionОбязательноВерсия установленного APK (передаётся при логине, обновляется при смене)
last_user_idОпциональноПоследний логинившийся сотрудник (для аудита)
current_user_idОпциональноАктивная сессия — null если нет
last_seen_atАвтоHeartbeat при каждом запросе
revoked_atОпциональноSoft-удаление админом (force-logout)
created_atАвто

KDS Session

Активная сессия повара после PIN-логина. Хранится в Auth Service / pos-bff side (сессия + JWT).

  • device_id — на каком устройстве
  • user_id — кто залогинен
  • selected_station_ids — массив UUID станций, выбранных при логине
  • started_at, last_activity_at, closed_at
  • expires_at — auto-логаут через 30 мин неактивности
  • JWT-токен живёт пока сессия активна

§2. Бизнес-правила

2.1. Регистрация и привязка устройства

  1. Первый запуск: приложение генерирует UUID устройства, сохраняет в SQLite. Показывает экран «Регистрация»: ввод email + пароля владельца/менеджера → выбор ТТ из списка ТТ его франшизы → сохранение device_id + franchise_id + store_id в БД через POST /api/v1/pos/kds/devices/register.
  2. Привязка к ТТ зафиксирована: смена ТТ — отдельное действие в Settings с подтверждением паролем и ре-регистрацией устройства.
  3. PIN-логин повара: после регистрации экран PIN. PIN ищет сотрудника в этой ТТ (PIN уникален per-ТТ согласно Сотрудники).

2.2. Multi-station при логине

После PIN — экран multi-select станций франшизы. Повар может выбрать одну или несколько. Например, маленькое кафе кладёт один планшет на «Кухня + Бар»; большая кухня держит планшеты только на «Горячий цех» каждый.

2.3. Видимость заказа

Заказ появляется на KDS если выполняются все условия:

  • order.store_id = device.store_id (та же ТТ)
  • order.status = 'accepted' (кассир нажал «Начать готовку» на POS — см. Заказы)
  • В заказе есть позиция с product.requires_kitchen=true И product.kitchen_station_id IN session.selected_station_ids

Заказ уходит с экрана когда:

  • Все позиции выбранных станций перешли в kitchen_status=ready
  • Или заказ перешёл в ready / closed / cancelled

Невыбранные станции этого же заказа отображаются в карточке серым (контекст: «там тоже что-то готовят»), без управления.

2.4. Статусы позиций

Каждая позиция (order_items) имеет kitchen_status:

pending (создана) → preparing (повар тапнул "В работе") → ready (повар тапнул "Готово")

Откат ready → preparing запрещён на KDS. Если ошибка — отмена через POS кассиром (полная отмена заказа или возврат после).

2.5. Кнопка «Готово» по станции

В карточке заказа — отдельный блок per-станция и отдельная кнопка «Готово». Активна когда все позиции этой станции в статусе ready. Тап → массовое закрытие, заказ уезжает с экрана если это была последняя выбранная станция.

2.6. Цвет карточки по времени

Каждый заказ имеет expected_ready_at. Цвет карточки на KDS:

  • Зелёныйnow < expected_ready_at - yellow_threshold_minutes
  • Жёлтыйnow ≥ expected_ready_at - yellow_threshold_minutes
  • Красныйnow ≥ expected_ready_at + red_threshold_minutes (просрочка)

Пороги — per-станция в Кухонных станциях (yellow_threshold_minutes, red_threshold_minutes).

Если в заказе позиции нескольких выбранных станций — берётся минимум (самая «жёсткая» окраска побеждает).

На устройстве в Settings можно локально переопределить пороги (для тестирования).

2.7. Звуковое + визуальное оповещение

  • На каждый новый заказ — звук
  • Звук повторяется каждые new_order_repeat_seconds (per-франшиза, default 30) пока повар не открыл карточку
  • При просрочке — другой, более тревожный звук
  • Звуки и интервал — из Настройки KDS (per-франшиза)
  • На устройстве можно локально переопределить громкость и отключить звук вообще

2.8. Отмена заказа в процессе готовки

Если кассир отменил заказ когда тот был на KDS:

  1. Звуковой сигнал «Заказ отменён»
  2. На карточке заказа появляется красное оверлей-уведомление «Отменён» на 2–3 секунды
  3. Карточка автоматически уезжает с экрана
  4. Логи остаются в Order Service для аудита (отменённые заказы не теряются)

2.9. Жизненный цикл сессии

stateDiagram-v2
    [*] --> unregistered : Первый запуск
    unregistered --> registered : Email админа + выбор ТТ
    registered --> session_active : PIN + multi-select станций
    session_active --> registered : Logout / auto-logout 30 мин
    session_active --> session_active : Sleep / wake (короткий блок экрана)
    registered --> registered : Reboot устройства (требует PIN заново)
    registered --> revoked : Админ удалил устройство в админке
    revoked --> unregistered : Повторная регистрация (email + ТТ)

Поведение:

  • После reboot устройства — обязательный повторный PIN (security)
  • После sleep/wake (короткий блок) — auto-resume если JWT не истёк
  • После 30 мин неактивности — auto-logout, требуется новый PIN
  • После «Logout» — сессия закрыта, экран PIN

2.10. Permissions

  • kds.access — обязательное право для PIN-логина в KDS. Без него — 403 KDS_ACCESS_DENIED.
  • kds.settings.edit — право менять локальные настройки устройства (звуки/пороги/layout) и регистрировать новые устройства / удалять.

См. Роли.

2.11. Multi-tenancy

В JWT есть franchise_id. pos-bff фильтрует все запросы (заказы, станции, настройки) по этому полю — пересечение между франшизами невозможно.

2.12. Settings sync

В P0 — pull: устройство забирает настройки франшизы (звуки, пороги, статус «удалено» соседних станций) при:

  • Каждом логине
  • Перезапуске приложения
  • Явном тапе «Применить настройки» в Settings

Live push через WebSocket — отложен в P1.

2.13. Транспорт (P0)

  • HTTPS REST + WSS WebSocket к https://erp-test.nirbi.ru/api/v1/pos/...
  • Live-обновления через WS-канал wss://...kds/stream?station_ids=...
  • При потере связи — красный баннер «Нет связи». Заказы продолжают отображаться (in-memory кэш). Действия пользователя складываются в очередь и синхронизируются при reconnect.
  • В Settings — переключатель транспорта Облако / Локальная сеть / Авто. В P0 рабочий только «Облако», остальные disabled с подсказкой «Будет в BR 5.2».

2.14. In-app updater

  • В Settings — кнопка «Проверить обновления»
  • Auto-check каждые 24 часа в фоне (опционально, настраивается)
  • Tauri-updater plugin: проверяет manifest на https://updates.nirbi.ru/kds/latest.json → если новая версия → показывает dialog → скачивает APK → ставит (Android запросит подтверждение пользователя один раз)
  • На pilot-этапе очень упрощает выкатку патчей

§3. Сценарии использования

3.1. Утренний запуск кухни

  1. Повар включает планшет на креплении
  2. Открывает KDS (или приложение уже автозапущено и держит экран)
  3. Вводит PIN
  4. Выбирает галочку «Горячий цех» из списка станций франшизы
  5. Видит экран «Ожидание заказов» с названием станции

3.2. Маленькое кафе на одном планшете

  1. Бариста-повар включает планшет
  2. PIN
  3. Выбирает галочки «Кухня» и «Бар»
  4. Заказы обоих станций отображаются вместе; в карточке группировка «Кухня: …», «Бар: …»; у каждой группы своя кнопка «Готово»

3.3. Новый заказ

  1. Кассир пробивает «Карбонара + Цезарь» в POS
  2. Кассир тапает «Начать готовку» — заказ → accepted
  3. Order Service публикует order.status_changed (Kafka)
  4. pos-bff (Kafka consumer) видит событие, фильтрует подписчиков по station_ids, пушит через WS
  5. На KDS «Горячий цех» ≤2 сек появляется зелёная карточка с «Карбонара» (Цезарь — на «Холодном цехе», в карточке отображается серым)
  6. Звучит звуковой сигнал, повторяется каждые 30 сек

3.4. Готовка

  1. Повар открывает карточку (звук перестаёт повторяться)
  2. Тапает «В работе» на «Карбонара» → kitchen_status=preparing → синий цвет
  3. Готовит, тапает «Готово» → kitchen_status=ready → зелёный с галочкой
  4. Кнопка «Готово» по станции «Горячий цех» становится активной (это была последняя позиция)
  5. Тап → все позиции «Горячего цеха» закрываются; так как других выбранных станций в этом заказе у повара нет — заказ уезжает с экрана
  6. POS у кассира видит «Заказ готов к подаче»

3.5. Просрочка

  1. Заказ висит дольше нормы (now > expected_ready_at - 5 мин)
  2. За 5 мин до срока — карточка становится жёлтой
  3. После срока — красной + тревожный звук
  4. Повар видит, ускоряется или связывается с менеджером

3.6. Просмотр техкарты

  1. Стажёр не помнит рецепт «Карбонары»
  2. В карточке тап «?» рядом с блюдом
  3. Открывается модалка с техкартой (название, ингредиенты, порядок, фото если есть)
  4. Возврат к карточке

3.7. Отмена заказа

  1. Гость передумал, попросил отменить
  2. Кассир в POS отменяет (cancelled)
  3. На KDS звук «Заказ отменён», красное оверлей-уведомление 2–3 сек
  4. Карточка уезжает с экрана

3.8. Потеря соединения

  1. Wi-Fi отключился
  2. KDS показывает красный баннер «Нет связи»
  3. Текущие заказы остаются на экране (in-memory кэш)
  4. Повар тапает «Готово» — действие в очередь
  5. Wi-Fi вернулся → KDS отправляет очередь → синхронизация → баннер «Связь восстановлена»

3.9. Конец смены

  1. Повар тапает «Меню» → «Logout»
  2. Сессия закрыта, экран PIN

§4. Жизненный цикл позиции на KDS

order_items.kitchen_status:

  pending ───[повар тапнул "В работе"]──→ preparing ───[повар тапнул "Готово"]──→ ready
                                                                                    │
                                                                                    ▼
                                                                        (откат запрещён)

При создании позиции (одновременно с accepted заказа) — kitchen_status=pending. KDS отображает позиции с requires_kitchen=true и фильтром по выбранным станциям.


§5. Экраны (общий обзор)

Детали каждого экрана — в Frontend Specs KDS (создаются в Шаге 3 workflow).

ЭкранНазначение
Регистрация устройстваEmail + пароль владельца → выбор ТТ → сохранение device_id
PIN-логинВвод 4-значного PIN + multi-select выбранных станций
Список заказовКарточки или список (выбирается в Settings); фильтр по выбранным станциям; цвет по времени; звук на новый заказ
Карточка заказаПозиции группированы по станциям; кнопка «Готово» per-станция; модификаторы; комментарий гостя; «?» → техкарта
Модалка техкартыНазвание, ингредиенты, последовательность; опц. фото
SettingsЗвук, громкость, локальные пороги, layout, transport, in-app updater, смена ТТ, logout

§6. Ролевая матрица

ДействиеВладелец франшизыВладелец партнёраМенеджерПовар/БаристаКассир
Зарегистрировать устройство (привязать к ТТ)✅ (свои ТТ)
PIN-логин в KDS
Видеть заказы выбранных станций
Менять статус позиции
Закрыть «Готово» по станции
Менять локальные настройки устройства
Сменить ТТ устройства✅ (свои)

Permissions:

  • kds.access — базовая (выдаётся поварам, барменам, менеджерам)
  • kds.settings.edit — управление локальными настройками + регистрация нового устройства

См. Роли.


§7. Hardware

7.1. Минимальные требования

  • Android 10+ (API 29+)
  • 10–13” экран, 1280×800+
  • 3+ ГБ RAM, 32+ ГБ хранилища
  • Wi-Fi 5GHz; опц. Ethernet через USB-C для стабильности

7.2. Рекомендованный список

СегментМодельПримерная цена
БюджетLenovo Tab M10 (3rd Gen), Xiaomi Redmi Pad SE15–20K ₽
MidSamsung Galaxy Tab A9+, Huawei MatePad 1125–35K ₽
Industrial (жирные кухни)ELO Touch Tablet (IP54)60K+ ₽

7.3. Что докупает владелец

  • Крепление (стенное / подвесное / штатив) — 1.5–5K ₽
  • Зарядное 2А+ с кабелем — 1–2K ₽
  • Опц. USB-Ethernet — 1K ₽
  • Опц. UPS на роутер

§8. Что НЕ входит в P0

  • LAN-hub режим (через POS Desktop) — BR 5.2
  • Полный offline-first (SQLite-репликация state, event-log sync) — BR 5.3
  • Печать кухонных чеков на ESC/POS принтерах — BR 5.4
  • Возврат / отмена заказа с кухни — требует финансовой логики, риск в MVP
  • Курсы подачи (горячее/холодное/десерт) — BR 5.6
  • Bump bar (USB HID) — BR 5.5
  • Загрузка кастомных звуковых файлов (только выбор из встроенных) — BR 5.7
  • Live push настроек франшизы через WS — P1 (в P0 — pull)
  • iOS-сборка — только Android в P0
  • Английский UI — только русский — BR 5.9

§9. In-app updater (детали)

Для пилота критично уметь выкатывать патчи без визита в каждую ТТ.

  • Manifest: https://updates.nirbi.ru/kds/latest.json{ version, apk_url, signature, release_notes, min_required_version }
  • Tauri-updater plugin: при тапе «Проверить обновления» (или auto-check 24ч) скачивает manifest, сравнивает с текущей app_version, если новая — показывает dialog с release notes
  • Установка: APK скачивается в Tauri scope, запускается Android intent для установки. Пользователь подтверждает один раз («Установить из этого источника»).
  • Откат: автоматического роллбэка нет в P0 — если новая версия плохая, заливаем patch-версию вручную.

В P1 — auto-rollback и canary-релизы (5% устройств → проверка → 100%).


§10. Связи с другими модулями


§11. Ссылки