food-market/docs/sprint-incident-1791428701.md
nns 87eb48cca9
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
docs(incident): menu-walk demo-cashier 403 — root cause + фикс + retest
Инцидент #1791428701 (quality-watchdog): кассир получал 403 на /api/money/accounts
на странице смен. Фикс — узкий /api/money/shifts/accounts (см. 9dc033d). Retest на
стенде: menu-walk 3/3 роли зелёные.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:40:44 +05:00

4.4 KiB
Raw Blame History

Инцидент quality-watchdog #1791428701 — menu-walk, demo-cashier 403

  • Время: 2026-10-08T08:05:01+05:00
  • Шаг watchdog: menu_walk — stage-ui-menu-walk.spec.ts › demo-cashier.
  • Подряд падений: 2.
  • Стенд: https://test.admin.food-market.kz

Симптом

E2E «каждый пункт меню для каждой демо-роли без 4xx/5xx/toast.error/console.error» падал на роли demo-cashier:

NET: 403 GET https://test.admin.food-market.kz/api/money/accounts?pageSize=100&isActive=true

(4 раза — заход на пункт меню + reload). Воспроизведено локально: npx playwright test scenarios/stage-ui-menu-walk.spec.ts -g "demo-cashier". Sidebar кассира: / /dashboard /catalog/products /inventory/stock /sales/retail /money/shifts /help /whats-new. Падало на /money/shifts.

Root cause

Страница смен CashShiftsPage (/money/shifts) для выбора кассы/эквайринга при открытии смены дёргала GET /api/money/accounts (хук useMoneyAccounts(true)). Этот эндпоинт — под правом MoneyView. Но у роли «Кассир» (DevDataSeeder.SeedEmployeeRolesAsync) по требованию владельца MoneyView = false («кассир не видит денег»), есть только CashShiftOperate = true. Итог: кассир может открыть смену, но страница падала 403 при загрузке списка счетов.

Эндпоинты самих смен (/api/money/shifts List/Get/Report/open/close/drawer) уже принимали право по ИЛИ-семантике MoneyView OR CashShiftOperate, а список счетов — нет. Это несоответствие и было дырой (пред-существующий баг модуля «Деньги», проявился на стенде). Не связано с другими правками спринта.

Исправление

  • Новый узкий эндпоинт GET /api/money/shifts/accounts (RequiresPermission("MoneyView,CashShiftOperate")): возвращает только активные наличные кассы + эквайринг, минимальные поля (id, name, kind, storeId, currency) — без остатков и банковских счетов. Кассиру хватает для выбора кассы при открытии смены, но модуль «Деньги» (остатки/платежи/чужие счета) по-прежнему недоступен — инвариант «кассир не видит денег» сохранён.
  • Веб: CashShiftsPage использует новый хук useShiftAccounts() вместо useMoneyAccounts(true). Админ-страница счетов (/api/money/accounts) не тронута.

Файлы: CashShiftsController.cs (+эндпоинт), web/src/lib/money.ts (+useShiftAccounts), web/src/pages/money/CashShiftsPage.tsx.

Тесты

tests/food-market.IntegrationTests/PermissionTests.cs (Testcontainers):

  • Cashier_can_list_shift_accounts_but_not_money_accounts — кассир (CashShiftOperate, без MoneyView): GET /api/money/shifts/accounts → 200 (только kind 1/4, без банка); GET /api/money/accounts → 403 (инвариант).
  • Me_permissions_for_cashier_role — /api/me permissions кассира (ProductsView есть, advanced-only скрыт в простом режиме). Результат: PermissionTests 4/4 зелёные.

Деплой и retest

  • ~/deploy-stage.sh — OK: guard isolation OK, api+web собраны/запушены, stage-api Healthy, https://test.admin.food-market.kz OK.
  • Ручной retest menu-walk demo-cashier на стенде — 1 passed (40.8s), 0 ошибок (ранее 403 на /api/money/accounts). Прогон всех трёх ролей (admin/ keeper/cashier) — зелёный (admin/keeper не задеты: их страница смен теперь тоже через /shifts/accounts, у них есть MoneyView по ИЛИ).

Коммит

9dc033d fix(money): кассир открывает смену без доступа к модулю «Деньги» (403).