Chain Admin → POS → KDS — Test Plan
Сквозная цепочка: каталог/настройки во франш-админке → меню и оформление заказа на POS → отображение и прогон статусов на KDS. Цель — ловить баги маршрутизации (kitchen_station_id), пропагации (стопы, прейскурант, модификаторы), денормализации позиций и status-flow между сервисами.
Зона: cross-cutting (T2 = Каталог-источник, T3 = ТТ/Заказы-приёмник). Прогон совместный или одним тестером. Приоритет: этот пакет = регрессия №1 после любого изменения в Catalog Service, Order Service, POS, KDS или pos-bff (WebSocket). AREA-код:
CHAIN(новый, добавлен к перечню вindex.md).
Контекст архитектуры
Admin (Franchise back-office)
│ REST/CRUD
▼
Catalog Service ──Kafka──► catalog.product.upserted/deleted
catalog.modifier_group.upserted/deleted
catalog.kds_settings.updated (BR 5.1)
│
│ GET /menu (BR 1.16) — вычисляемое меню ТТ с прейскурантом и стоп-листом
▼
POS Desktop (Tauri) ──── pos-bff (Node/Fastify)
│ POST /orders, /items, status changes (Order Service)
│ WS subscribe: kds_settings, stop-list, menu invalidation
▼
Order Service: new → in_progress → ready → closed | cancelled
│
│ pos-bff WebSocket broadcast
▼
KDS Desktop ── фильтрация по kitchen_station_id ── статусы позиций
Подготовка стенда
| # | Что | Как |
|---|---|---|
| P1 | Учётки | Franchise admin, Cashier (PIN на POS), KDS-оператор (PIN). Получить у тест-лида |
| P2 | ТТ с привязкой | 1 ТТ с активным прейскурантом, минимум 2 кухонные станции (Кухня, Бар) |
| P3 | Тестовый каталог | Минимум: 2 «кухонных» товара (requires_kitchen=true, kitchen_station_id=Кухня), 1 «барный» (Бар), 1 «не-кухонный» (requires_kitchen=false — например бутылочный напиток), 1 товар с группой модификаторов |
| P4 | Стоп-лист | Чистый перед прогоном (GET /stop-list?store_id=X → []) |
| P5 | KDS-экраны | Открыты 2 окна: фильтр Кухня и фильтр Бар. Залогинены под PIN |
| P6 | DevTools | На POS — Network/WS вкладка для проверки событий. На KDS — то же |
Этап 1 — Admin → POS (пропагация каталога)
Изменение в админке → проверка на POS (refresh меню или WS-инвалидация).
| TC | Действие в админке | Ожидание на POS | Severity при баге | Bug-якорь |
|---|---|---|---|---|
| TC-CHAIN-001 | Создать новый товар, привязать к категории, активировать | Появляется в меню ТТ (после рефреша / WS-обновления) с корректной ценой из дефолтного прейскуранта | Critical | — |
| TC-CHAIN-002 | Изменить name товара | На POS отображается новое имя; в открытом несохранённом заказе — старое (денормализация — см. этап 4) | Major | — |
| TC-CHAIN-003 | Изменить base_price товара | Если в прейскуранте ТТ нет переопределения — на POS новая цена. Если есть override — цена из прейскуранта | Critical | связано с BUG-050 |
| TC-CHAIN-004 | Soft-delete товара | Пропадает из меню POS. В уже созданных заказах (new) — позиция остаётся | Critical | — |
| TC-CHAIN-005 | Восстановить soft-deleted товар | Появляется в меню POS | Major | — |
| TC-CHAIN-006 | Снять галочку «Доступно во всех точках» в карточке товара | Товар пропадает с POS этой ТТ | Major | BUG-031 (галочка возвращается после Save) |
| TC-CHAIN-007 | Установить флаг is_admin_only | Товар не виден на POS под ролью Cashier | Major | — |
| TC-CHAIN-008 | Установить is_open_price | На POS при добавлении в заказ — диалог ввода цены, без цены не добавляется | Major | — |
| TC-CHAIN-009 | Установить is_by_weight | На POS — диалог ввода веса; total = price × weight | Major | — |
| TC-CHAIN-010 | Установить is_alcohol / is_tobacco | На POS — соответствующее поведение (возрастная блокировка / спец-чек) | Minor | — |
| TC-CHAIN-011 | Деактивировать категорию | Все товары категории прячутся с POS | Major | BUG-040 (активация не каскадная — проверить обратное) |
| TC-CHAIN-012 | Изменить порядок категорий в админке | Порядок отображения на POS меняется | Minor | — |
| TC-CHAIN-013 | Создать модификатор и привязать к товару | На POS у товара появляется блок модификаторов с min/max | Major | — |
| TC-CHAIN-014 | Установить min=1, max=2 для группы | На POS нельзя добавить товар без выбора, нельзя выбрать >2 | Major | BUG-044 (валидации может не быть) |
| TC-CHAIN-015 | Удалить группу модификаторов в админке (привязанная к товару) | На POS у товара модификаторов нет; в открытом заказе — модификаторы остаются (денорм) | Major | — |
| TC-CHAIN-016 | Изменить цену опции модификатора | На POS новая цена при выборе опции в новой позиции | Major | — |
| TC-CHAIN-017 | Создать прейскурант, привязать к ТТ, переопределить цену товара | На POS этой ТТ — цена из прейскуранта | Critical | BUG-051 (привязка ТТ к прейскуранту может не работать) |
| TC-CHAIN-018 | Изменить цену в прейскуранте | На POS новая цена для новых позиций; в new-позициях — старая (денорм) | Major | — |
| TC-CHAIN-019 | Прейскурант: цена = 0 на товаре | Товар на POS с ценой 0 (или скрыт по бизнес-правилу — уточнить со спекой) | Minor | — |
| TC-CHAIN-020 | Поставить товар в стоп-лист ТТ | На POS товар грейется/блокируется. Кнопка «Добавить» неактивна | Critical | связан с BUG-049 |
| TC-CHAIN-021 | Снять товар со стоп-листа | На POS товар снова доступен | Critical | — |
| TC-CHAIN-022 | Поставить категорию в стоп-лист | Все товары категории заблокированы на POS | Major | — |
| TC-CHAIN-023 | Стоп-лист поставлен во время оформления заказа (товар уже в позиции new) | Позиция остаётся, но добавить ещё нельзя | Major | exploratory — race condition |
| TC-CHAIN-024 | Изменить настройки KDS франшизы (/admin/kds/settings) | KDS-экраны при «Применить» / перезапуске подтягивают свежие настройки. WS-событие catalog.kds_settings.updated доставляется (P1) | Major | BR 5.1 |
Cross-cut Этапа 1 — sanity
- Под Cashier (POS) недоступны admin-only товары
- Меню POS соответствует
GET /api/v1/catalog/menu?store_id=X(BR 1.16) — сверить через DevTools - При смене прейскуранта без перезапуска POS — нужна инвалидация (если её нет — баг)
Этап 2 — POS → KDS (маршрутизация заказа)
Создание заказа на POS → проверка появления и фильтрации на KDS.
| TC | Сценарий | Ожидание | Severity | Тип |
|---|---|---|---|---|
| TC-CHAIN-030 | Cashier создаёт заказ по столику с 1 кухонным товаром | Заказ появляется на KDS-Кухня в течение ≤ 2 сек. Не появляется на KDS-Бар | Critical | Smoke |
| TC-CHAIN-031 | Заказ только из «не-кухонных» товаров (requires_kitchen=false) | На KDS не появляется ни на одном экране | Major | Functional |
| TC-CHAIN-032 | Заказ-микс: кухня + бар | Кухонная позиция → KDS-Кухня; барная → KDS-Бар. Заказ-обёртка с одним номером | Critical | Functional |
| TC-CHAIN-033 | Заказ-микс: кухня + не-кухонный | На KDS-Кухня — только кухонная позиция, не-кухонная не светится | Major | Edge |
| TC-CHAIN-034 | Товар с модификаторами (выбраны 2 опции) | На KDS видны: имя товара, выбранные опции, цена (или без цены — уточнить UX) | Major | Functional |
| TC-CHAIN-035 | Позиция с комментарием кассира («без лука») | Комментарий отображается на KDS под названием | Major | Functional |
| TC-CHAIN-036 | Заказ с указанием стола №X | KDS показывает №стола / зону / номер заказа | Major | Functional |
| TC-CHAIN-037 | Quantity > 1 одного товара | На KDS — отображение либо «×N», либо N карточек (по UX-спеке) | Minor | Functional |
| TC-CHAIN-038 | Два кассира одновременно создают заказы на одну ТТ | Оба заказа появляются на KDS, нет коллизии номеров (per-store per-day) | Critical | Edge |
| TC-CHAIN-039 | Заказ создан на ТТ-A | На KDS ТТ-B он не появляется (multi-tenancy) | Critical | Negative/RBAC |
| TC-CHAIN-040 | Изменить kitchen_station_id товара в админке после появления на KDS | Существующая позиция остаётся на исходном KDS (денорм). Новые позиции — на новой станции | Major | Edge |
| TC-CHAIN-041 | Удалить позицию из заказа в new на POS | Позиция исчезает с KDS | Critical | Functional |
| TC-CHAIN-042 | Добавить позицию в new на POS (заказ уже отправлен) | Позиция появляется на KDS на нужной станции | Critical | Functional |
| TC-CHAIN-043 | KDS оффлайн (отключить сеть на 30 сек), создать 2 заказа, восстановить сеть | После reconnect — KDS подтягивает пропущенные заказы. Если не подтягивает — баг (P0 KDS pull / WS replay) | Critical | Edge |
| TC-CHAIN-044 | Refresh KDS (F5 / перезапуск Tauri) | Текущие активные заказы восстанавливаются из бэка | Major | Edge |
Этап 3 — Status-flow (POS ↔ KDS ↔ Order Service)
| TC | Действие | Ожидание во всех 3 местах |
|---|---|---|
| TC-CHAIN-050 | KDS отмечает «Взято в работу» по позиции/заказу | Order: new → in_progress. POS: индикатор «готовится». KDS: позиция перекрашивается |
| TC-CHAIN-051 | KDS отмечает «Готово» (все позиции/заказ) | Order: in_progress → ready. POS: уведомление «заказ готов». KDS: уезжает с активного экрана (или в архив) |
| TC-CHAIN-052 | Кассир закрывает заказ на POS (выдача) | Order: ready → closed. KDS не показывает |
| TC-CHAIN-053 | Кассир отменяет заказ в new (через UI POS) | Order: → cancelled. KDS: позиция исчезает |
| TC-CHAIN-054 | Кассир отменяет заказ в in_progress | Order: → cancelled с причиной. KDS: получает уведомление об отмене (визуально) |
| TC-CHAIN-055 | Попытка отменить closed заказ | API: отказ (по Order docs — нельзя). UI POS: кнопка отмены недоступна |
| TC-CHAIN-056 | Попытка добавить позицию в in_progress | API: 422 / 409. UI POS: блокирует |
| TC-CHAIN-057 | KDS откатывает «Готово» обратно (если функция есть) | Order: ready → in_progress. POS: индикатор обратно. Если функции нет — задокументировать как Missing |
| TC-CHAIN-058 | Per-position статусы: одна позиция готова, вторая нет | Заказ остаётся in_progress пока не готовы все. Только после последней — ready |
| TC-CHAIN-059 | KDS-Кухня закрыл свою позицию, KDS-Бар — нет | Заказ in_progress. KDS-Бар продолжает видеть свою. После закрытия Бара — ready |
| TC-CHAIN-060 | Race: два KDS жмут «Готово» по одной позиции одновременно | Идемпотентность. Один 200, второй — 200 или 409 (no-op). Не должно быть двойного списания/двойного события |
| TC-CHAIN-061 | Per-store per-day numbering: создать заказ в 23:59, оплатить в 00:01 | Номер по дате создания. С 00:00 нумерация сбрасывается (BR Order #1) |
Этап 4 — Денормализация / data integrity
Цель: убедиться что изменения в каталоге не переписывают историю.
| TC | Сценарий | Ожидание |
|---|---|---|
| TC-CHAIN-070 | Создан заказ → изменить name товара → открыть заказ на POS | Старое имя в позиции; новое имя — только в меню |
| TC-CHAIN-071 | Создан заказ → изменить цену в прейскуранте → открыть заказ | Старая unit_price в позиции; total не пересчитан |
| TC-CHAIN-072 | Создан заказ с модификатором → изменить цену опции → открыть заказ | Старая цена опции в позиции |
| TC-CHAIN-073 | Создан заказ → soft-delete товар → открыть заказ | Позиция остаётся видимой и оплачиваемой |
| TC-CHAIN-074 | Создан заказ → удалить группу модификаторов → открыть заказ | Модификаторы в позиции остаются (по referenced_by_product_ids / по денорму) |
| TC-CHAIN-075 | Создан заказ → переименовать категорию → KDS | На KDS — category_path на момент создания позиции (или live — уточнить с архитектурой) |
| TC-CHAIN-076 | Полный shift-report за день | Суммы соответствуют сумме paid_amount всех closed заказов. RRN/способ оплаты на месте |
Этап 5 — RBAC / multi-tenancy
| TC | Роль | Действие | Ожидание |
|---|---|---|---|
| TC-CHAIN-080 | Cashier ТТ-A | POST /orders с store_id=ТТ-B | 403 |
| TC-CHAIN-081 | Cashier ТТ-A | GET /orders ТТ-B | 403 / пусто |
| TC-CHAIN-082 | KDS-оператор ТТ-A | Видит заказы ТТ-A только | OK |
| TC-CHAIN-083 | Manager | POST /orders/{id}/complete | OK (по Order docs) |
| TC-CHAIN-084 | Manager | POST /orders/{id}/cancel чужой ТТ | 403 |
| TC-CHAIN-085 | Cashier | Отмена чужого заказа в своей ТТ | 403 (только свой по Order docs) |
| TC-CHAIN-086 | Franchisee | GET /orders по ТТ другой ЮЛ | 403 |
| TC-CHAIN-087 | PIN-логин на POS без pos.access permission | Отказ | |
| TC-CHAIN-088 | KDS-PIN-логин под Cashier-PIN (если KDS-PIN отдельный) | По спеке — отдельная permission или OK? Зафиксировать факт |
Этап 6 — Edge / exploratory / поиск багов
| TC | Сценарий | Что ловим |
|---|---|---|
| TC-CHAIN-100 | Создать пустой заказ (без позиций) → попытка оплатить | 422 / UI блокирует |
| TC-CHAIN-101 | Заказ с 50+ позиций | Производительность KDS, не падает |
| TC-CHAIN-102 | Комментарий 1000+ символов | Не ломает UI KDS, не обрезает |
| TC-CHAIN-103 | Имя товара 255 символов без пробелов | Аналог BUG-041 — не ломает KDS-вёрстку |
| TC-CHAIN-104 | POS оффлайн → создание заказа | Если offline-first ещё не реализован (Phase 2.3) — корректное сообщение, не silent fail |
| TC-CHAIN-105 | Catalog Service down → POS открыт | Меню кешируется или ошибка — не белая страница |
| TC-CHAIN-106 | Order Service down → попытка создать заказ | 5xx обработан UI, кассир уведомлён |
| TC-CHAIN-107 | Изменить kitchen_station_id на товаре, по которому в in_progress уже находятся 5 позиций на старой станции | Старые остаются на старой, новые — на новой. Никакого «магического переноса» |
| TC-CHAIN-108 | Создать заказ → перезапустить pos-bff (рестарт WS) → KDS должен переподключиться | Heartbeat / auto-reconnect отрабатывает |
| TC-CHAIN-109 | KDS показывает заказ; в админке кто-то soft-delete’ил товар; кассир пытается отменить позицию | Не должно ломать ни POS, ни KDS |
| TC-CHAIN-110 | Quantity = 0 / отрицательное / дробное на штучном товаре | 422 |
| TC-CHAIN-111 | Цена опции модификатора = NULL → выбор опции | Опция бесплатная, не падает |
| TC-CHAIN-112 | Time-jump: системные часы на POS уехали на 1 час → создание заказа | paid_at от сервера, не от клиента — корректно |
Регрессионный пул (P0)
Минимальный круг после любого изменения в Catalog / Order / pos-bff / POS / KDS:
- TC-CHAIN-001, 020, 021 (каталог→POS базовое + стопы)
- TC-CHAIN-030, 032, 039 (POS→KDS маршрутизация + multi-tenancy)
- TC-CHAIN-050, 051, 052, 053 (полный status-flow по одному заказу)
- TC-CHAIN-070, 071 (денормализация)
- TC-CHAIN-080, 082 (RBAC по заказам/KDS)
Время прогона: ~25 минут опытным тестером.
Что зафиксировать в репорте прогона
RUN-{date}-CHAIN-{N}
- Тестировщик: {name}
- Версия (commit/тег): {git sha или дата деплоя}
- Прогнано: {N} / 60 кейсов
- Pass: {N} | Fail (новые): {BUG-NNN, ...} | Fail (известные): {BUG-NNN, ...}
- WS-события (sample): catalog.kds_settings.updated — доставлено / нет
- Latency POS→KDS p50 / p95 (sec): {x} / {y}
- Решение: Pass / Pass-with-warnings / Fail
Открытые вопросы (уточнить у тимлида перед прогоном)
- WS vs polling — KDS получает заказы через WS, polling или гибрид? От этого зависит поведение TC-CHAIN-043, 108
- Per-position vs per-order статусы — KDS управляет позициями отдельно или заказом целиком? От этого зависит TC-CHAIN-058, 059
- Откат «Готово» на KDS — поддерживается? (TC-CHAIN-057)
- Что с
kitchen_station_id-денормом — копируется на момент создания позиции или live (TC-CHAIN-040, 075) - Оффлайн POS — реализовано или Phase 2.3 ещё не начат? (TC-CHAIN-104)
- PIN для KDS — отдельный или общий с POS Cashier? (TC-CHAIN-088)