Сессия 2026-05-12 — Полный обход admin-панели на проде

Переключились на прод-стенд admin.nirbi.ru/admin (см. memory project_prod_stand_admin.md). Старая регрессия POS/KDS на erp-test.nirbi.ru (F66-F76) заморожена до возврата к POS/KDS.

Цель

Последовательно пройти все разделы admin-панели на проде, найти пользовательские баги/проблемы. Без POS и KDS интеграции — это позже.

Скоуп этой сессии

Разделы по порядку (план уточним по факту первой навигации):

  1. Логин → дашборд / стартовая
  2. Юридические лица / Франшизы
  3. Торговые точки (ТТ)
  4. Сотрудники + Роли + Права
  5. Каталог: категории, товары, модификаторы, прейскуранты, стоп-листы
  6. Меню / доступность
  7. Склад / поставки (если есть в этом стенде)
  8. Заказы (admin view, не POS)
  9. Отчёты / аналитика
  10. Настройки

⛔ Не трогаем: POS-flow, KDS-flow, оплаты через PayKeeper.

Сетап

  • Учётка: admin@erp.local / <password — см. private/creds.md> (Администратор, all_franchise)
  • Браузер: Playwright (chromium)
  • Скриншоты: test-base/screenshots/2026-05-12-admin-prod-walkthrough/

Карта sidebar (снято 2026-05-12)

Dashboard                           /admin
Юридические лица                    /admin/legal-entities
Торговые точки                      /admin/stores
Сотрудники
  ├ Список                          /admin/employees
  ├ Роли                            /admin/roles
  ├ Расписание                      /admin/schedule
  ├ Шаблоны смен                    /admin/schedule/templates
  ├ Активность                      /admin/activity/employees
  ├ Терминалы                       /admin/activity/terminals
  ├ Формулы зарплаты                /admin/payroll/formulas
  └ Ведомости                       /admin/payroll
Каталог
  ├ Товары                          /admin/catalog/products
  ├ Модификаторы                    /admin/catalog/modifiers
  ├ Категории                       /admin/catalog/categories
  ├ Прейскуранты                    /admin/catalog/price-lists
  ├ Временные тарифы                /admin/catalog/time-tariffs
  ├ Расписание меню                 /admin/catalog/menu-availability
  ├ Внешние меню                    /admin/external-menus
  ├ Ингредиенты                     /admin/catalog/ingredients
  ├ Стоп-листы                      /admin/catalog/stop-lists
  └ Кухонные станции                /admin/catalog/kitchen-stations  ← KDS-adjacent, скип в этой сессии
Склад
  ├ Остатки                         /admin/warehouse/inventory
  ├ Приёмки                         /admin/warehouse/receipt-acts
  └ Списания                        /admin/warehouse/write-off-acts
Заказы
  ├ Активные                        /admin/orders/active
  ├ История                         /admin/orders/history
  ├ Очередь кухни                   /admin/kitchen                   ← KDS, скип
  ├ Журнал транзакций               /admin/transactions
  └ Мониторинг смен                 /admin/shifts/monitor
Финансы
  └ Чаевые (Нетмонет)               /admin/tips
Настройки KDS                                                         ← скип
  └ Звуки и пороги                  /admin/kds-settings
Устройства                                                            ← POS/KDS, скип
  └ POS и KDS                       /admin/devices

Скоуп этой сессии (после снятия карты)

В работе: Юр.лица, ТТ, Сотрудники (всё), Каталог (без «Кухонные станции»), Склад, Заказы (без «Очередь кухни»), Финансы.

Откладываем до возвращения к POS/KDS: Кухонные станции, Очередь кухни, Настройки KDS, Устройства.

Что сделали — chronological log

Этап 0: Логин + dashboard (2026-05-12 ~11:26 MSK)

  • URL https://admin.nirbi.ru/admin/login → форма «Войдите в систему»
  • Логин admin@erp.local / <password — см. private/creds.md>https://admin.nirbi.ru/admin (dashboard «Добро пожаловать, Admin!»)
  • Console: только 2× 404 на /favicon.ico — отдельная мелочь, см. ниже.
  • На dashboard виден блок «Информация о пользователе»: Роль: «—» (прочерк), Franchise ID: (пусто). У admin-пользователя должна быть роль или хотя бы что-то осмысленное.

Этап 1: Снятие карты sidebar

  • Раскрыл все collapsible-секции, см. карту выше.

Этап 2: Юр.лица — список + карточка главного ЮЛ + создание

Список /admin/legal-entities — таблица из 3 baseline-записей:

  • ООО Франшиза Главное (Франшиза, Активен, 1 ТТ, Главное)
  • ИП Иванов Партнёр (Франчайзи, Активен, 0 ТТ)
  • ООО Петров и Ко (Франчайзи, Приостановлен, 0 ТТ)

Фильтры: поиск по названию/ИНН, статус (Все/Активен/Приостановлен), тип (Все/Франшиза/Франчайзи). Кнопки «Импорт» и «+ Создать».

Карточка ЮЛ /admin/legal-entities/10000000-0000-0000-0000-000000000001 — 3 вкладки:

  • Реквизиты ✓ — Основные данные / Банковские / Контакты (поля Банк/БИК/Р-с/К-с/Телефон/Email все «—»)
  • Торговые точки ✓ — заглушка «Привязано 1 ТТ. Перейти к списку →» (без фильтра по ЮЛ)
  • PayKeeper ✓ — info-блок «PayKeeper теперь настраивается на странице ТТ»

В backlog (BUG-005) упоминались вкладки «Информация», «Сотрудники», «Документы» — их в текущем UI нет, заменены на Реквизиты/ТТ/PayKeeper. BUG-005 устарел.

🐛 F76 — Ссылка «1 ТТ» в блоке Основные данные ведёт на список ЮЛ. href="/admin/legal-entities" вместо /admin/stores (с фильтром по ЮЛ или без). Пользователь кликает «1 ТТ», ожидая увидеть ТТ — попадает обратно в тот же список ЮЛ. Скрин: F76-le-card-1tt-link-broken.png. Severity: 🟢 Minor (не блокирует, но запутывает). Где смотреть: admin-spa, компонент карточки ЮЛ, в блоке «Основные данные» поле «Торговые точки» — <Link to="/admin/stores?legal_entity_id=..."> или хотя бы /admin/stores. Сейчас просто /admin/legal-entities.

Редактирование ЮЛ (/edit) — форма открывается без ошибок (BUG-006 не воспроизводится у admin-пользователя — возможно фикснут, возможно специфичен для другой роли — verify позже). Тип ЮЛ и Статус в форме не редактируются.

Создание ЮЛ (/new) — wizard в 2 шага. Шаг 1: только «Франчайзи» доступен (логично — Головное уже создано). Шаг 2 — большая форма (Основные/Банк/Контакты/Договор/Владелец/Права).

🐛 F77 — Форма создания франчайзи не валидирует форматы ИНН/ОГРН/Email на клиенте. При нажатии «Создать» с явно невалидными значениями (ИНН=12345 5 цифр, ОГРН=1234567 7 цифр, Email ЮЛ=notanemail, Email владельца=owner_no_at_sign) — UI не показывает ни одной ошибки формата. Единственная ошибка — Дата договора: Обязательное поле (т.к. она пустая). Если бы дата была заполнена, форма попыталась бы отправиться. Скрин: F77-le-new-no-format-validation.png. Severity: 🟡 Major. Где смотреть: admin-spa, форма /admin/legal-entities/new. Нужна клиентская валидация:

  • ИНН: ровно 10 или 12 цифр (placeholder уже намекает)
  • ОГРН: ровно 13 или 15 цифр
  • Email (и ЮЛ, и владельца): regex/HTML5 type=email
  • БИК/Р-с/К-с: ровно 9 / 20 / 20 цифр (если значения заданы) Также проверить серверную валидацию — fallback на 400. Не submit’ил на прод (создаст мусорный ЮЛ), нужно согласие пользователя для теста с реальной отправкой.

Хорошее (✓): при выходе из формы с несохранёнными изменениями браузер показывает confirm Есть несохранённые изменения. Покинуть страницу?. UX корректен.

Этап 3: Торговые точки

  • Список /admin/stores: 1 ТТ «ТТ Smoke 001», Москва, ООО Франшиза Главное, Опубликована.
  • Карточка ТТ: 6 вкладок (Информация ✓, Меню ✓ — пусто, Терминалы ✓ — 1 терминал «Терминал 1» ФН/РН ККТ, Интеграции ❌, Столы ✓ — пустая карта, Кухня — пропущена как KDS).
  • 🐛 F78/admin/stores/<id> → вкладка Интеграции → GET /api/v1/admin/paykeeper/accounts/by-store/<id>500. UI «Не удалось загрузить данные PayKeeper». Severity 🟡 Major. Скрин F78-store-integrations-paykeeper-500.png. Где смотреть: payments-service, handler paykeeper/accounts/by-store/{store_id}. Вероятно отсутствует обработка случая «нет привязки» — должен возвращать 200 с null, не 500.
  • Форма /admin/stores/new: required-поля используют HTML5 native validation (browser popup) — отличается от формы ЮЛ с кастомными сообщениями. Inconsistency UX, PROPOSAL.
  • Время в карточке ТТ выводится с секундами (09:00:00) — UX-мелочь, PROPOSAL.

Этап 4: Сотрудники (Список/Роли/Расписание/Шаблоны/Активность/Терминалы/Формулы/Ведомости)

  • Список сотрудников /admin/employees: 2 сотрудника — Admin Admin (Администратор), Тест Тестов ("Кассир" — с кавычками).
  • Роли /admin/roles: 2 роли — "Кассир" (Пользовательская) и Администратор (Системная). Карточка роли: 4 таба прав (Общее/Back-office/POS/Зарплата).
  • 🐛 F79 — Названия с кавычками везде: роль "Кассир", товар "Латте 0.3", категория "Кофе", модификатор "Молоко", ингредиент "Молоко 3.2%", прейскурант "Базовый". Скорее всего — общий input-component не trim’ит и не запрещает кавычки в начале/конце. Severity 🟢 Minor (сквозной). Скрин F79-role-name-with-quotes.png. Где смотреть: общий form-input компонент для названий, либо отдельные валидаторы при создании. Нужен trim() + запрет открывающих/закрывающих кавычек.
  • 🐛 F80 — Stale JWT (~15 мин), SPA не редиректит на login и не обновляет токен. При истечении API возвращают 401, экраны рендерятся пустыми / с ошибками («Не удалось загрузить»). Severity 🟡 Major. Воспроизведено при открытии /admin/roles/<id> после 15+ мин активности. Скрин F80-jwt-expired-no-redirect.png. Где смотреть: admin-spa HTTP-клиент — нет интерсептора 401 → logout/redirect. Также нет refresh-token flow (если планируется).
  • Форма создания роли /admin/roles/new: поле «Название» без required атрибута — клиент пропустит пустое название. Замечание к F79.
  • Расписание/Шаблоны смен/Активность/Терминалы/Формулы зарплаты/Ведомости — открываются без 500. Замечание: в «Формулы зарплаты» формула с типом «По роли», но поле «Роль/Сотрудник» = «—» (неполные данные).

Этап 5: Каталог

  • Товары /admin/catalog/products: 1 товар "Латте 0.3". Карточка: 3 вкладки (Информация/Модификаторы/Техкарта).
  • Подмечено: техкарта "Латте 0.3"Выход: 0 г, при наличии ингредиента 0.2л. Скорее всего unit-конверсия (л → г) не сделана, или просто баг расчёта.
  • Модификаторы: 1 модификатор "Молоко" (Группа, 3 опции).
  • Категории: 1 категория "Кофе".
  • Прейскуранты: 1 прейскурант "Базовый" (По умолчанию). Назначено ТТ: 0, но в карточке «ТТ Smoke 001» поле «Прейскурант» = «По умолчанию (дефолтный)». Совпадает с backlog BUG-051 (default применяется неявно). Подтверждаю верность.
  • Временные тарифы / Расписание меню / Внешние меню / Ингредиенты / Стоп-листы — открываются без ошибок.
  • Кухонные станции — пропущено (KDS).

Этап 6: Склад

  • Остатки/Приёмки/Списания — все открываются, пусто, без ошибок.

Этап 7: Заказы

  • Активные/История/Журнал транзакций/Мониторинг смен — открываются, пусто, без ошибок. «Очередь кухни» — пропущено (KDS).

Этап 8: Финансы → Чаевые (Нетмонет)

  • 🔴 F81 — Mock-данные в проде. /admin/tips отображает: Кофейню Тверскую, Кофейню Арбат, 4 «официантов» (Анна Белова, Борис Котов, Елена Сидорова, Максим Петров), десятки «заказов» №№ 12, 23, 75, 95, 104, …, 479 — всех нет ни в одном другом разделе системы. На франшизе только ТТ «ТТ Smoke 001» и 2 сотрудника (Admin Admin, Тест Тестов).
  • Сетевой анализ: при загрузке страницы — ни одного запроса к /api/.../tips-подобным эндпоинтам. Только /api/v1/admin/auth/me. Данные захардкожены во фронтенде.
  • Severity: 🔴 Critical. На проде пользователь видит фейковые финансовые цифры (13 950 ₽ «общая сумма чаевых», топ «официантов», 45 транзакций). Может принять рабочие решения на основе мусора.
  • Скрин: F81-tips-fake-data-or-leak.png. Где смотреть: admin-spa, маршрут /admin/tips — компонент содержит hardcoded array. Нужно: убрать mock, заменить на реальный API-вызов; если API ещё не готов — показать «Раздел в разработке» / скрыть пункт из sidebar.

Findings этой сессии

IDSevЗонаTitleStatus
F76🟢legal-entitiesСсылка «1 ТТ» в карточке ЮЛ ведёт на /admin/legal-entities (сам список)open
F77🟡legal-entitiesФорма создания франчайзи: нет клиентской валидации ИНН/ОГРН/Emailopen
F78🟡stores/integrationsТТ → Интеграции → 500 на GET paykeeper/accounts/by-store/<id>open
F79🟢catalog/employees/rolesНазвания с кавычками везде (роль, товар, категория, ингредиент, модификатор, прейскурант) — нет trim/sanitizeopen
F80🟡auth-rbacStale JWT (15 мин), SPA не редиректит на login, нет refreshopen
F81🔴finance/tipsЧаевые (Нетмонет) показывают frontend-hardcoded mock-данные на продеopen

PROPOSAL:

  • Inconsistent form validation UX (ЮЛ — кастомные ошибки, ТТ — HTML5 native).
  • Время в карточке ТТ показывается с секундами 09:00:00.
  • Поле «Название роли» в форме создания не required на клиенте.

Подтверждение backlog:

  • BUG-005retracted (UI карточки ЮЛ переделан: Реквизиты/ТТ/PayKeeper вместо Информация/Сотрудники/Документы).
  • BUG-006 не воспроизводится у admin (специфично для другой роли — verify-needed под Manager/Franchisee).
  • BUG-051 подтверждён: default-прейскурант применяется неявно, ТТ не имеет привязки в БД.

Наблюдение dashboard:

  • Поле «Роль: —» для admin-пользователя — by-design или баг? Не критично, отложено.

Этап 9: Глубокий проход — E2E связность, edge-cases, состояния, каскады

После reality-check от пользователя — проход «по верхам» дополнен глубокими сценариями. Создан [TEST] товар + [TEST] категория, проверены связность, фильтры/поиск, edge-данные, удаление/восстановление, каскад.

Создание [TEST] объектов

  • Создан товар [TEST] Капучино 0.4 (POST /catalog/products → 201, id d1224622-a76d-4bce-bee8-64bf2cc3411e).
  • Создана категория [TEST] Кофейная (через + Добавить категорию с leading/trailing пробелами [TEST] Кофейная пробелы обрезаны при сохранении).
  • Привязана категория к товару через edit-форму — сохранилось.

Связность (положительная)

  • ✓ [TEST] товар появился в поиске стоп-листов сразу.
  • ✓ [TEST] категория появилась в селекте категории товара сразу.
  • ✓ После привязки — фильтр товаров «Все категории» → [TEST] Кофейная корректно фильтрует.
  • ✓ Поиск по «капу» (нижний регистр) находит «Капучино» — case-insensitive работает.
  • ✓ Inline toggle статуса (Активен/Неактивен) в списке товаров — работает.
  • ✓ Восстановление из «Удалённые» товары — работает (связь с категорией сохраняется).

Каскады

  • ✓ Удаление категории, к которой привязан товар → 422 «Невозможно удалить: в категории есть товары». Backend защищает, UI показывает понятное сообщение. Хороший пример обработки 422.

Новые находки этого этапа

IDSevWhereWhat
F82🟢catalog/products/newПоля Вес брутто/нетто/Калории/Белки/Жиры/Углеводы не имеют min=0. Принимают -5, -100 без HTML5-валидации. (Поля «Сортировка» и «Время приготовления» min=0 настроены — inconsistency)
F83🟢catalog/products/newПоле «Цвет (HEX для POS)» принимает любой текст («не хекс») — нет pattern-валидации
F84🟡catalog/products/new«Сохранить» беззвучно ничего не делает при HTML5-инвалид (popup не виден если поле вне viewport). На форме ЮЛ — кастомные сообщения. Inconsistency
F85⚠️ XSS-warningcatalog/products/newНазвание принимает <script>alert(1)</script> без sanitization на клиенте. Серверная sanitization не проверена. Если render через innerHTML — XSS-risk
F86🔴 / BUG-060stores/menuGET /api/v1/admin/catalog/menu/<store>500. UI показывает «Нет товаров в меню» вместо ошибки → скрывает 500 за пустым состоянием. BUG-060 подтверждён
F87🟢catalog/products/Карточка товара не отображает поле «Категория», даже если назначена. В списке колонка есть
F88🟢catalog/categoriesConfirm-диалог удаления оборачивает имя в кавычки: Удалить категорию ""Кофе""? — двойные кавычки на F79-данных. Visual glitch

Дополнения к старым находкам

  • F79 уточнён: trim для пробелов работает на категории — но кавычки "..." остаются в названиях везде. То есть существует выборочная санитизация (trim есть, dequote — нет). Также backend принял <script> в названии товара (F85).
  • F80 повторно подтверждён: через 15 мин 401 на /api/v1/admin/auth/me без редиректа на login.

Не покрыто этим заходом (нужен отдельный)

  • Concurrency (правка одного объекта в 2 вкладках) — не сделано.
  • Permissions/RBAC под Manager/Franchisee — отложено (требует владельца партнёра, а пользователь сказал «без новых аккаунтов»).
  • E2E «ТТ → опубликовать → снять → удалить» — частично.
  • Поиск/фильтры везде — частично (только товары).
  • Длинные строки (1000+ символов), большие числа, дата в будущем — не до конца.
  • Загрузка файлов (фото товара, импорт ЮЛ.xlsx) — не трогал.

Артефакты [TEST]-объектов

  • Товар [TEST] Капучино 0.4 (id d1224622-a76d-4bce-bee8-64bf2cc3411e) → в «Удалённые» товары.
  • Категория [TEST] Кофейная (id e450d896-4d92-4ad2-9c4a-82cc1c590b00) → удалена насовсем.

Этап 10: Расширенный проход (после reality-check #2)

После повторного вопроса «почему остановились» продолжен глубокий проход.

[TEST] ТТ flow (Опубликовать/Снять/Удалить + edge)

  • Создана [TEST] ТТ Капучино (id 157dffc5-bc7f-4c43-8905-03b5abb9268f) с минимально валидными данными.
  • ✓ Опубликована → статус «Опубликована», кнопка «Снять с публикации».
  • ✓ Снято → статус снова «Черновик». Состояние «Приостановлена» из фильтра — отдельный flow, не воспроизводилось через этот UX.
  • ✓ Удалена в конце прохода.
  • 🐛 F89 — селект «Прейскурант» в edit ТТ: две опции с пометкой «(дефолтный)» (null-placeholder + реальный default). Путает.
  • ℹ️ Поле «Юридическое лицо» в edit — disabled с подписью «ЮЛ нельзя изменить после создания». ✓ Хорошо.

Concurrency (2 вкладки)

  • Открыл одну ТТ в двух вкладках, изменил имя в обеих, сохранил последовательно.
  • 🐛 F90 — Last-write-wins: вторая правка тихо затёрла первую. Нет optimistic-lock, нет диалога «данные изменились». Major.

Edge-данные на форме ТТ

  • 🐛 F91 (CRITICAL) — PATCH ТТ с длинным именем (516 char + 🔥💩 + ёёё) → 500. UI беззвучен — пользователь стоит на /edit без feedback. Backend не валидирует длину строки.
  • F92 / +: HTML5 type="email" валидирует email в форме ТТ. Это положительно — но inconsistency с формой ЮЛ (там F77 пропустила email без @).

Backlog regressions (подтверждены)

  • 🐛 F93 / BUG-018-019 — Save шаблона смены → 500 (PUT /api/v1/admin/shift-templates/<id>). UI беззвучен. Backlog подтверждён.
  • 🐛 F94 / BUG-023 — Save формулы зарплаты «По роли» → 500 (PUT /api/v1/admin/salary-formulas/<id>). Backlog подтверждён.
  • 🐛 F95 / BUG-024 — Экспорт CSV ведомости → 422. URL составлен криво: ?store_id=&period=2026-05/export (пустой store_id + period с прилипшим /export). Backlog подтверждён.
  • BUG-007 verify-needed — Поиск сотрудников по «Тест» нашёл «Тест Тестов» под admin. Возможно фикс или специфично для другой роли.
  • BUG-002 verify-needed — Фокус в input не теряется после первого символа (поверка на форме «Сотрудника»). Не воспроизводится у admin.

Прейскуранты и каталог

  • Создан [TEST] Премиум прейскурант, удалён. Confirm-диалог «Это действие необратимо» ✓.
  • 🐛 Save цен в прейскуранте без UI-feedback — нет «Сохранено» или toast. Мелочь.
  • 🐛 F98 (CRITICAL) — Кнопка «Дублировать» товар → 500 (POST products/<id>/duplicate). UI беззвучен.
  • 🐛 F99 (CRITICAL) — Кнопка «+ Дочерняя» категория создаёт на корневом уровне, parent_id не сохраняется. Иерархия категорий сломана.

Роли и i18n

  • Тест дубликата имени роли → backend 409 ✓. Но:
  • 🐛 F96 — Backend возвращает английскую ошибку «Role with this name already exists», UI выводит её сырой. Локализация ошибок не настроена.
  • ✓ Клиентская валидация роли с пустым именем → русское «Название роли обязательно» — локализовано.
  • 🐛 F97 — «Отмена» в форме /admin/roles/new идёт history.back (на предыдущую страницу), а не на список ролей.

Этап 11: Финальный добор (после второго reality-check)

Набивка для пагинации

  • Создано 30 [TEST] товаров через тот же endpoint /api/v1/admin/catalog/products что и UI. Поле в payload — unit_of_measure (snake_case).
  • ✓ Пагинация работает: «Показано 1–20 из 33», 2 страницы, кнопки Назад/Вперёд.
  • ✓ Сортировка «Название A-Я» работает: " < [ (Unicode 34 vs 91), «Латте 0.3» идёт первым, потом [TEST]....
  • ✓ Все 30 [TEST] товаров удалены batch-DELETE’ом по тому же endpoint.

Удалённые роли

  • Создан + удалён [TEST] DeleteMe → появился в «Удалённые роли».
  • ✓ «Восстановить» из ⋮ меню работает, роль возвращается в активные.
  • Окончательно удалён 2-м DELETE.

Внешние меню

  • Создано [TEST] Меню для ТВ → /admin/external-menus//edit. Видны шаблоны (Сетка/Список/Слайдер/Текст), каталог товаров, привязка к ТТ, выбор прейскуранта (динамический/конкретный).
  • Удалено.
  • Подмечено: «Скачать HTML» доступно у draft-меню до публикации (возможно by-design).

Временные тарифы

  • Открыл /admin/catalog/time-tariffs/<store> → «+ Создать тариф» → попытка submit без дней недели → ✓ кастомное сообщение «Выберите хотя бы один день недели».

Расписание меню

  • Открыл /admin/catalog/menu-availability/<store> → «+ Добавить расписание» → submit без названия → ✓ кастомное сообщение «Имя обязательно».

PayKeeper-импорт сотрудников

  • 🔴 F100/admin/employees/import-from-paykeeperGET /api/v1/admin/paykeeper/accounts?status=active502 Bad Gateway. UI шумит «Нет активных интеграций PayKeeper», скрывая 5xx. Связано с F78. Возможно весь PK-сервис down или routing сломан.

Импорт ЮЛ

  • ✓ Скачивание шаблона legal_entities_template.xlsx работает.
  • Реальный импорт не тестировал (нужен файл).

AI-улучшение товара

  • 🐛 F101 — Кнопка «Улучшить с помощью AI» в редакторе товара кликается, но никакой реакции (ни запроса, ни модалки, ни тоста). Фича не имплементирована, но UI показывает кнопку.

Артефакты этапа

  • [TEST] Капучино 0.4 (id d1224622-...) — остался в soft-delete. Hard-delete 404 (видимо такого endpoint нет). Подкупает админ почистит позже.
  • Стенд чист от других [TEST] объектов.

Положительные подтверждения (этап 10)

  • ✓ Backend защищает от каскадных конфликтов (категория с товарами → 422 с понятным сообщением).
  • ✓ Уникальность имени роли (409 + текст).
  • ✓ Confirm-диалоги на деструктивных действиях, с пометкой «Это действие необратимо».
  • ✓ Trim leading/trailing пробелов в категории.
  • ✓ PIN-валидация: блокирует буквы и >4 цифр.
  • ✓ Восстановление из «Удалённые» сохраняет связь с категорией.
  • ✓ Поиск товаров: case-insensitive, по части слова.
  • ✓ Фильтр по категории работает.
  • ✓ Inline toggle статуса в списке товаров работает.
  • ✓ ЮЛ нельзя изменить у существующей ТТ (disabled-поле).
  • ✓ Шаблон XLSX для импорта ЮЛ скачивается корректно.
  • ✓ Цены модификаторов в прейскуранте отображаются с привязкой к товару.

Этап 12: Подготовка к POS/KDS

Регистрация устройств

Сегодня же на стенде поднимали POS и KDS:

  • POS-устройство POS-814800 на ТТ Smoke 001 — Online (после починки модуля POS-onboarding силами Сано через накат erp-pos).
  • KDS — после диагностики через Grafana + Loki (nginx access-logs в namespace ingress-nginx) обнаружили баг ingress-роутинга: Tauri-клиенты с Referer: tauri.localhost маршрутизировались на pos-bff независимо от path. POST /api/v1/admin/auth/login → pos-bff → 404 «Route not found». Сано пофиксил в коде KDS-клиента. После переустановки KDS зарегистрировался.

Детальный док по KDS-блокеру: test-base/archive/KDS-login-route-not-found-2026-05-12.md.

Назначения admin’у на ТТ

Чтобы PIN-логин на KDS работал, понадобилось:

  • Задать PIN admin’у = 9999 (PATCH employees) — UI висел F102/F103, фактически записалось.
  • Привязать роль «Администратор» admin’а к ТТ Smoke 001 (раньше «Магазины: —»). UI снова висел F102, прошло через прямой PATCH с { roles: [{ role_id, store_ids: [...] }] }.

Этап 13: Наполнение стенда для POS/KDS-прогона

Запрос пользователя: «Наполнить меню реалистичными данными, чтобы работал весь функционал, включая временные акции».

Что было создано (через UI + batch через тот же endpoint что UI)

СущностьКоличествоДетали
Категории12Кофе, Напитки, Напитки холодные, Горячие напитки, Завтраки, Супы, Горячее, Гарниры, Салаты, Закуски, Десерты, Выпечка
Ингредиенты13Молоко 3.2%, Говядина, Куриное филе, Свинина, Форель, Картофель, Лук, Морковь, Мука, Яйцо, Сахар, Соль, Сыр
Модификаторы5 (14 опций)Молоко (Обычное/Безлактозное/Овсяное), Сироп (Ваниль/Карамель/Шоколад), Размер порции (Малая/Средняя/Большая), Соус (Сметана/Майонез/Кетчуп/Горчица), Без сахара
Товары67Распределены по категориям 4–9 в каждой; после удалили «Капустный с морковью» (дубль)
Цены в прейскуранте «Базовый»82Товары + модификатор-опции, диапазон 80–400 ₽ (random)
Временные тарифы3Завтрак −15% (07:00–11:00 Пн-Вс / категория Завтраки), Бизнес-ланч −25% (12:00–16:00 Пн-Пт / Супы+Горячее+Гарниры), Счастливый час −10% (14:00–17:00 Пн-Пт / Кофе+Десерты)
Расписание меню1Завтраки 07:00–11:00 Пн-Вс — категория Завтраки скрывается из POS вне окна
Стоп-лист2«Латте 0.3» (Тест, ранее) + «Уха из форели» (Нет рыбы)
Картинки товаров68 (все)64 из Wikipedia (точное соответствие блюду), 4 — из Unsplash (товары без подходящих Wiki-статей)

Корректировки по картинкам и категориям (после ревью пользователя)

  • Перенесены из Кофе в Горячие напитки: Какао, Чай чёрный пакетированный, Чай зелёный пакетированный, Чай чёрный в чайнике.
  • Прицельно переснаряжены картинки: Куриная лапша (суп с курицей), Запечённая форель (готовое блюдо), Рис отварной, Макароны (спагетти), Овощи на пару (рататуй), Соленья ассорти (соленья), Слойка с сыром (хачапури), 3 пирожка (разные).

Наблюдение по архитектуре (не баг — но в memory)

  • Картинки товаров принимаются POS-меню только из minio.nirbi.ru. PATCH image_url с внешним URL (Unsplash) → admin-bff пишет в БД, но POS-меню эту картинку не отдаёт. Загрузка должна идти через POST /api/v1/admin/catalog/products/<id>/image с FormData — backend кладёт в MinIO и возвращает minio-URL.

Итог

✅ Все 9 разделов admin-панели (без POS/KDS) пройдены:

  • Юр.лица (полностью)
  • Торговые точки (включая карточку, без вкладки «Кухня»)
  • Сотрудники — 8 подпунктов
  • Каталог — 9 подпунктов (без «Кухонные станции»)
  • Склад — 3 подпункта
  • Заказы — 4 подпункта (без «Очередь кухни»)
  • Финансы — 1 подпункт

🆕 6 новых находок (F76–F81), 1 retracted (BUG-005), 1 подтверждение (BUG-051), 1 verify-needed (BUG-006).

Артефакты:

  • Скрины: test-base/screenshots/2026-05-12-admin-prod-walkthrough/ (6 файлов).
  • Эта сессия: test-base/sessions/2026-05-12-admin-prod-walkthrough.md.