Демо-стенд «Шаурма Арбат»

Полная синтетика на тестовом VPS erp-test.nirbi.ru. Создана 2026-05-04 через admin-bff/POS-bff API (заводилось скриптом, эмулирующим UI).

Логины

РольEmailПароль
Admin (главное ЮЛ)admin@erp.localadmin123
Партнёр-владелецpartner@test.localTest123!
Менеджер ТТivan@test.localTest123! (PIN 1111)
Кассир Арбатanna@test.localTest123! (PIN 2222)
Кассир Сокольникиoleg@test.localTest123! (PIN 3333)
Бариста Арбатmaria@test.localTest123! (PIN 4444)
Курьерpetr@test.localTest123! (PIN 5555)

ЮЛ + ТТ

ТТЮЛUUID ТТ
Арбат-флагманглавное (10000000-…001)fe4b54a9-2cc1-458f-9d0e-338bbc51df76
Сокольникиглавное6e02dffe-e344-4859-909b-68059cb39385
Партнёр — Бауманскаяc64da7bd-7534-4c75-809b-bcc7e71abe8747c128b9-1f7d-4f01-b6ad-795795b50e7a

График: 10:00–22:00 (Бауманская до 23:00), Europe/Moscow.

Каталог

  • 8 категорий: Шаурма / Бургеры / Снеки / Напитки → {Холодные, Горячие → {Кофе}} / Десерты
  • 20 ингредиентов
  • 3 кухонные станции: Холодный цех / Горячий цех / Бар
  • 4 группы модификаторов: Размер (S/M/L), Соус, Без чего-то, Дополнительно
  • 15 продуктов: 4 шаурмы, 2 бургера, 2 снека, 2 хол.напитка, 3 кофе, 2 десерта — все с фото (Unsplash)
  • 11 техкарт (для всех dish-продуктов) — с заполненными cold/hot loss% (мясо 25%, овощи 5%, лепёшка 3%, кофе 10%)
  • 5 modifier-tech-cards: M (+25г курицы), L (+50г курицы), +Мясо (+100г), +Сыр (+30г), +Соус (+30мл)
  • Все 11 dish-продуктов имеют assembly_time (45-240с) и kitchen_station_id → kitchen ETA считается корректно
  • 1 прейскурант «Базовый» (default) с ценами товаров и опций
  • 3 time-tariffs (happy hours): Утренний кофе -20% (8-11 пн-пт), Бизнес-ланч -15% (12-15 пн-пт), Happy Hour напитки -10% (17-19 пт-вс)
  • 2 menu-availabilities: Завтрак (Кофе 7-12), Десерты (14-22)

Цены (₽): Шаурма Классическая 290, Острая 310, Говядина 350, Сырная 320, Чизбургер 250, Двойной 350, Картофель 120, Наггетсы 180, Кола 80, Сок 120, Эспрессо 100, Латте 180, Капучино 170, Чизкейк 220, Маффин 120. Размер: M +50, L +100. Дополнительно: Сыр +50, Мясо +100, Соус +30.

Склад

3 warehouses (по одному на ТТ, auto-bootstrap при создании ТТ). 3 receipt acts проведены — стартовые поставки (Арбат — полная, Сокольники — 70%, Бауманская — 50% от Арбата).

Stock balances: 60 строк (20 ингредиентов × 3 склада).

Зал

17 zal_tables: 8 на Арбате (4 на 4 чел + 4 на 6 чел), 5 на Сокольниках, 4 на Бауманской.

Зарплата

3 salary formulas:

  • Менеджер ТТ — 60 000 ₽/мес фиксированный, норма 160 ч
  • Кассир — 250 ₽/час + 1.5x overtime (375 ₽/ч)
  • Курьер — 200 ₽/час + 1.5x overtime (300 ₽/ч)

20 shift_records (4 сотрудника × 5 рабочих дней прошлой недели, 10:00–18:30 с обедом 13:00–13:30).

PayKeeper

3 PaykeeperAccount (по одному на ТТ) + 3 PaykeeperTerminal — все с одинаковыми creds koala-test sandbox (общий с Koala_TG_app).

ТТaccount_idterminal_id
Арбатac1a270f-4230-401b-a5bb-88eead762c5b2e5af560-4b80-4afb-bb26-9780e54de667 (SYNTH-ARBAT-1)
Сокольникиc951bba0-e661-4af0-aa50-f83b0bec72b8637c9a03-540a-4a62-8e10-1a3e2433ac49 (SYNTH-SOKOLNIKI-1)
Бауманскаяa2c34269-58dc-4a67-b476-97b55c07abbd00697ce5-9129-4318-a7f5-9f1dd1133370 (SYNTH-BAUMANSKAYA-1)

Webhook URLs (3 на каждый account: informer / refund / receipt) — например для Арбата:

  • https://erp-test.nirbi.ru/pk-webhooks/informer/ac1a270f-4230-401b-a5bb-88eead762c5b

Webhook в ЛК PK не переключён

ЛК koala-test всё ещё указывает на Koala_TG_app (https://user.koalanearby.ru/php/api/payment/webhook). Нужно согласовать с коллегой и переключить хотя бы для одного account_id (предлагается Арбат-флагман).

Стоп-листы

  • Арбат: «Шаурма Острая курица» (причина: «Закончился острый перец»)
  • Сокольники: категория «Десерты» (причина: «Нет в наличии»)

Оба проверены через POS-меню под cashier-токеном — товары/категории корректно скрыты.

POS-устройства + смены + заказы

DeviceUUID device_idТТ
POS-Арбат-1b0e4a21c-172c-4a12-a5ac-04a6aa50eaeaАрбат
POS-Сокольники-10ed454e5-e9c7-46d1-81df-0b3961e82241Сокольники

2 смены открыты (Анна на Арбате, Олег на Сокольниках, fiscal_cycle=1).

7 заказов созданы:

  1. Арбат cash 470₽ (Анна): Шаурма Классическая M + Соус чесночный + Кола 0.5
  2. Арбат card 550₽ (Анна, RRN: 123456789012): Чизбургер + Картофель + Латте
  3. Арбат cash 860₽ (Анна, клиент Дмитрий Иванов): 2× Шаурма Говядина + 2× Кола
  4. Арбат card 400₽ (Анна, RRN: 222333444555, клиент Светлана Петрова): Латте + Чизкейк
  5. Арбат cash 410₽ (Анна): Шаурма Классическая + Картофель
  6. Арбат cash 410₽ (Анна): Шаурма Классическая + Картофель
  7. Сокольники cash 550₽ (Олег): Шаурма с говядиной L + Острый соус + Эспрессо

Клиенты

5 customers:

  • Дмитрий Иванов (+79161112233) — есть заказ
  • Светлана Петрова (+79165554433) — есть заказ
  • Михаил Соколов (+79263011234)
  • Елена Морозова (+79166669900)
  • Алексей Волков (+79263030399)

4 customer_groups:

  • VIP (static)
  • Постоянные клиенты (dynamic, потратили ≥5000 ₽ за всё время)
  • Спящие (dynamic, ≥60 дней без заказа)
  • Майские именинники (dynamic, birthday_month=5)

Backup

Полные дампы всех 15 БД в ~/erp/backups/synth-pre-2026-05-04/ на VPS — точка отката.

Что не сделано (вне scope синтетики)

  • Webhook в ЛК PK не переключён на наш adapter (нужно согласование с коллегой Koala_TG_app)
  • DeliveryZones — нет UI и не созданы (заказы только takeaway/dine-in)
  • Закрытие смены / Z-отчёт — не делалось
  • POS terminals (фискальные терминалы в store_db.pos_terminals) — пусто, ФН-номера не привязаны (для физических касс)
  • Customer addresses — пусто (для delivery-заказов)
  • External menus / Aggregator bindings — пусто (Yandex.Eda / Delivery Club не подключены)
  • Price list assignment на ТТ → POS не пересчитывает цены (известный баг D.2PATCH stores/{id} {price_list_id} сохраняется в БД, но POS catalog menu не использует это поле; нужно копать в pos-bff fetchStorePriceListId)
  • Partial refund restock — реализован только full refund, partial требует мэппинг refund_cart на recipe_items (отдельная задача)
  • Modifier-tech-cards в auto-write-offOrderItem.modifiers хранит только group_name+option_name без modifier_option_id, поэтому списание модификаторов пропускается (отдельная задача — расширить ModifierEntry с UUID)

Что заработало после фиксов 2026-05-05

BUG-1 — auto-write-off ингредиентов. Закрытие заказа (POST /orders/{id}/complete) → событие order.completed → warehouse-service создаёт WriteOffAct со status=“posted” и автоматически списывает ингредиенты по техкарте.

BUG-2 — restock на refund. Refund заказа (POST /pos/refunds) → событие order.refundedis_full_refund=true) → warehouse-service создаёт обратный акт AUTO-RESTOCK-… и восстанавливает ингредиенты.

Изменения:

  • erp-order-service: payload order.completed/order.refunded теперь содержит items[] с product_id, product_name, quantity
  • erp-warehouse-service: новый OrderEventConsumer (Kafka-listener), OrderInventoryService (расчёт списания + создание акта), KafkaConfig с DLT/safeRecoverer

Known issues

— все обнаруженные баги Phase 1 исправлены 2026-05-05.

Исправлено сегодня (помимо BUG-1 и BUG-2)

  • D.2 Price list assignment на ТТUpdateStoreRequest не имел поля priceListId, PATCH stores игнорировал. Добавлено поле + mapping → проверено: Шаурма 290₽ → 145₽ при назначении Промо-50.
  • Customer summary 500OrderRepository.aggregateByCustomer{Since} объявлял возврат Object[] напрямую, Hibernate иногда оборачивал как Object[]{innerArray}, отсюда ClassCastException в OrderService:959. Исправлено: List<Object[]> + safe-unwrap helper firstRow(). Customer-service membership recompute теперь работает: Дмитрий (потратил 6110₽) после order.completed автоматически добавляется в группу «Постоянные клиенты» (≥5000₽).
  • POS-bff activeStoreId для owner of franchise / partnerrequireCashier middleware всегда брал storeIds[0] из scope.store_ids. Для scope all_franchise (admin@erp.local) и legal_entity_ids (партнёры) поле пустое, поэтому activeStoreId=null → URL /by-store/null → 400 INVALID_PARAMETER на всех табах кроме каталога. Фикс: для не-store_ids scope доверяем X-Active-Store header без проверки (бэкенд валидирует franchise_id ownership). Проверено — /pos/tables возвращает 8 столов Арбата под admin-токеном.
  • BUG-3 closeWithPayment не публиковал order.completedOrderService.closeWithPayment ставил status=closed + completedAt, но публиковал только order.paid. В результате auto-write-off, customer recompute и POS UI listeners (что слушают order.completed/order.closed/order.status.changed) не срабатывали для заказов закрытых через /pos/orders/{id}/close-with-payment — это dominant close path на desktop POS (быстрый кассовый flow). Фикс — добавил все 3 publish’а как в legacy close() (line 538-540). Проверено: после closeWithPayment Курица 29.850 → 29.550 → 29.400 кг при 2-х заказах (списание 0.150 кг × n шаурм).
  • BUG-4 markReadyInternal не выставлял order.ready_at + не двигал items.kitchen_status/pos/kitchen-queue/{id}/mark-ready менял только order.status="ready". order.ready_at оставался null, order_items.kitchen_status="pending". Это ломало kitchen-queue UI (фильтр по kitchen_status показывал заказ как «в работе») и репорты, которые джойнят ready_at. Фикс: установка order.ready_at=now, перебор всех order_items с kitchen_station_id != nullkitchen_status=READY + kitchen_ready_at=now. Проверено: после mark-ready оба item стали ready, ready_at=01:14:41.

Не дотестировано (отдельные задачи)

  • J Aggregator → Order — нет mock-webhook для имитации Yandex.Eda
  • L PayKeeper paid flow с реальным informer webhook — не переключено в ЛК koala-test (общий с Koala_TG_app)
  • A.4 quirk pos-bff activeStoreId = scope.store_ids[0] для manager с 3 ТТ — фронт не передаёт X-Active-Store header. Не баг, но плохо UX (остаётся для scope=store_ids — теперь header работает корректно для owner of franchise / partner)

Ссылки