From 87eb48cca918fcccf896d3552368f0c3e2e4bd23 Mon Sep 17 00:00:00 2001 From: nns Date: Thu, 8 Oct 2026 08:40:44 +0500 Subject: [PATCH] =?UTF-8?q?docs(incident):=20menu-walk=20demo-cashier=2040?= =?UTF-8?q?3=20=E2=80=94=20root=20cause=20+=20=D1=84=D0=B8=D0=BA=D1=81=20+?= =?UTF-8?q?=20retest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Инцидент #1791428701 (quality-watchdog): кассир получал 403 на /api/money/accounts на странице смен. Фикс — узкий /api/money/shifts/accounts (см. 9dc033d). Retest на стенде: menu-walk 3/3 роли зелёные. Co-Authored-By: Claude Opus 5.5 --- docs/sprint-incident-1791428701.md | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/sprint-incident-1791428701.md diff --git a/docs/sprint-incident-1791428701.md b/docs/sprint-incident-1791428701.md new file mode 100644 index 0000000..3fd6f23 --- /dev/null +++ b/docs/sprint-incident-1791428701.md @@ -0,0 +1,66 @@ +# Инцидент 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).