From 42645174e0a5a9afd7fe4c936783725da74749fc Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Fri, 8 May 2026 12:16:17 +0500 Subject: [PATCH] =?UTF-8?q?e2e:=20bugs-fixed=20=D0=BE=D1=82=D1=87=D1=91?= =?UTF-8?q?=D1=82=20=E2=80=94=20=D0=B2=D1=81=D0=B5=2012=20=D1=88=D0=B0?= =?UTF-8?q?=D0=B3=D0=BE=D0=B2=20=D0=B7=D0=B5=D0=BB=D1=91=D0=BD=D1=8B=D0=B5?= =?UTF-8?q?=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20fix=20HIGH+MEDIUM+2=20gap'?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Прогон против commit bac527d (4 fix-коммита): - Passed 10 → 12 (+2) - Failed 2 → 0 (−2) - Critical/HIGH/MEDIUM bugs 1+1 → 0+0 - Logic gaps 2 → 0 Все 4 пункта из full-pass.md закрыты. --- .../full-cycle-2026-05-08-bugs-fixed.md | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tests/e2e/reports/full-cycle-2026-05-08-bugs-fixed.md diff --git a/tests/e2e/reports/full-cycle-2026-05-08-bugs-fixed.md b/tests/e2e/reports/full-cycle-2026-05-08-bugs-fixed.md new file mode 100644 index 0000000..697a161 --- /dev/null +++ b/tests/e2e/reports/full-cycle-2026-05-08-bugs-fixed.md @@ -0,0 +1,170 @@ +# E2E report: full-cycle (bugs-fixed 2026-05-08) + +Запущен: 2026-05-08T07:14:43.536Z +Длительность: 13.7с + +**Итог:** 12 ✓ / 0 ✗ / 0 ⚠ / 0 ◯ (всего 12) + +## Дельты к full-pass.md (предыдущий прогон 2026-05-08) + +| Метрика | full-pass.md | bugs-fixed (этот) | Δ | +|---|---|---|---| +| Passed | 10 | 12 | +2 | +| Failed | 2 | 0 | −2 | +| Warnings | 0 | 0 | 0 | +| Skipped | 0 | 0 | 0 | +| Critical bugs | 0 | 0 | 0 | +| HIGH bugs | 1 (RetailSale oversell) | 0 | −1 | +| MEDIUM bugs | 1 (Supply без supplierId 500) | 0 | −1 | +| Logic gaps | 2 | 0 | −2 | + +### Что починено + +- **HIGH → fixed**: RetailSalesController.Post теперь проверяет `stock >= line.qty` (с агрегацией дублей одной позиции в чеке) под Serializable-транзакцией; при недостатке возвращает 409 со списком `{productId, productName, qty, available}`. Раньше qty=99999 при остатке 10 проходил `/post` → 204, остаток уходил в минус. +- **MEDIUM → fixed**: `RequiredGuid.FirstMissing(...)` в SuppliesController/RetailSalesController/ProductsController отсекает `Guid.Empty` на 400 с указанием поля. SaveChanges обёрнут в перехват `PostgresException 23503` → 400 вместо 500. +- **gap → fixed**: при bootstrap новой org через `SeedTenantReferencesAsync` создаётся системная `ProductGroup «Все товары»` (`IsSystem=true`, защищена от удаления; Phase5e-миграция). UI ProductEditPage / ProductQuickCreateModal авто-выбирают её для нового товара. +- **gap → fixed**: RetailSalesController.Create отказывается с 400 на пустом `lines[]`. UI RetailSaleEditPage держит кнопку «Сохранить» disabled с tooltip «Добавьте хотя бы одну позицию» если позиций нет. + +## ✓ Step step01_create_organization: SuperAdmin создаёт «Test Shop {timestamp}» (KZ, KZT, ФЛК телефона) + +Длительность: 1765мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | POST /api/super-admin/organizations → 200 | ✓ org=Test Shop 1778224483536 | +| api | GET /api/super-admin/organizations включает созданную org | ✓ | +| api | Невалидный phone отвергается | ✓ 400 | + +## ✓ Step step02_create_first_admin: SuperAdmin создаёт первого Admin сотрудника организации (Employee + AppUser) + +Длительность: 593мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | Temp password возвращён CreateOrgResult | ✓ len=12 | +| db | employees содержит ровно 1 запись для новой org | ✓ count=1 | +| db | AspNetUserRoles содержит role=Admin для нового user | ✓ Admin | + +## ✓ Step step03_login_as_admin: Логин под admin (не SuperAdmin override) — JWT с org_id и role=Admin + +Длительность: 571мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | /connect/token password-grant выдал токен | ✓ | +| api | /api/me содержит role=Admin | ✓ Admin | +| api | /api/me содержит правильный orgId | ✓ d19d968e-4a09-450b-8445-bbdfb67eb491 | + +## ✓ Step step04_create_storekeeper_and_cashier: Admin создаёт Storekeeper и Cashier через /settings/employees + +Длительность: 1738мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | employee-roles list | ✓ 200, total=3 | +| api | Системная роль «Кладовщик» существует | ✓ | +| api | Системная роль «Кассир» существует | ✓ | +| api | POST /api/organization/employees (Кладовщик) | ✓ 200 | +| api | POST /api/organization/employees (Кассир) | ✓ 200 | +| db | employees total = 3 (admin + keeper + cashier) | ✓ count=3 | +| api | Невалидный email отвергается при createAccount | ✓ 400 | + +## ✓ Step step05_login_as_cashier: Логин под Cashier — role-guard проверяется (sidebar/role guard) + +Длительность: 830мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | /api/me содержит роль соответствующую системной Cashier | ✓ Cashier | +| api | Cashier → GET /api/organization/employees → 403 | ✓ 403 | +| api | Cashier → GET /api/sales/retail — доступен | ✓ 200 | + +## ✓ Step step06_create_counterparty: Admin создаёт «ТОО Тест Поставщик» (БИН + телефон) + +Длительность: 533мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | POST /api/catalog/counterparties | ✓ 201 | + +## ✓ Step step07_ensure_main_store: Проверить что есть main store (из bootstrap), иначе создать + +Длительность: 158мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | GET /api/catalog/stores | ✓ 200 | +| db | Main store существует (от bootstrap) | ✓ Основной склад | + +## ✓ Step step08_create_supply: Admin создаёт Supply Draft (3-5 товаров) и проводит (Posted) + +Длительность: 3634мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | Создано 3 product (валидный barcode + price + group) | ✓ e2e Product 1 1778224483536, e2e Product 2 1778224483536, e2e Product 3 1778224483536 | +| api | Supply без supplierId → 400/409 | ✓ 400 {"error":"Поле SupplierId обязательно.","field":"SupplierId"} | +| api | Supply с пустым lines[] → 400 | ✓ 400 | +| api | POST /api/purchases/supplies (Draft) | ✓ 201 | +| api | POST /api/purchases/supplies/{id}/post (Draft → Posted) | ✓ 204 | +| api | Повторный post Supply → 409 (idempotency) | ✓ 409 {"error":"Документ уже проведён."} | +| db | stock_movements содержат запись на каждую строку Supply | ✓ count=3, expected=3 | + +## ✓ Step step09_check_stock_after_supply: GET /api/inventory/stock — quantity увеличился на supplied amount + +Длительность: 980мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | stock(e2e Product 1 1778224483536) +10 (было 0, стало 10) | ✓ delta=10, expected=10 | +| api | stock(e2e Product 2 1778224483536) +15 (было 0, стало 15) | ✓ delta=15, expected=15 | +| api | stock(e2e Product 3 1778224483536) +20 (было 0, стало 20) | ✓ delta=20, expected=20 | +| api | GET /api/inventory/stock без storeId возвращает строки на каждый склад | ✓ rows=1, stores=c9ddd2fe | +| db | stocks.Quantity == SUM(stock_movements.Quantity) для e2e Product 1 1778224483536… | ✓ sum_movements=10 stocks.Quantity=10 | +| db | stocks.Quantity == SUM(stock_movements.Quantity) для e2e Product 2 1778224483536… | ✓ sum_movements=15 stocks.Quantity=15 | +| db | stocks.Quantity == SUM(stock_movements.Quantity) для e2e Product 3 1778224483536… | ✓ sum_movements=20 stocks.Quantity=20 | + +## ✓ Step step10_ensure_retail_point: Проверить или создать розничную точку (кассу) + +Длительность: 205мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | RetailPoint существует | ✓ Касса 1 | +| api | RetailPoint с несуществующим storeId → 400/404 | ✓ 400 | + +## ✓ Step step11_create_retail_sale: Admin создаёт RetailSale, 2 позиции из приёмки, cash, Post + +Длительность: 1681мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | Продажа qty>остатка → /post должен 4xx | ✓ 409 | +| api | Продажа с отрицательным qty/price → 400 | ✓ 400 | +| api | discount=10 на line(price=100,qty=1) → lineTotal=90 | ✓ lineTotal=90 | +| api | POST /api/sales/retail (Draft) | ✓ 201 | +| api | POST /retail/{id}/post | ✓ 204 | +| api | Повторный post RetailSale → 409 | ✓ 409 | + +## ✓ Step step12_check_stock_after_sale: GET /api/inventory/stock — quantity уменьшился на sold amount + +Длительность: 962мс + +| Тип | Проверка | Результат | +|---|---|---| +| api | stock product=f4ea65fd… −2 (было 10, стало 8) | ✓ delta=2, expected=2 | +| api | stock product=288b2be1… −2 (было 15, стало 13) | ✓ delta=2, expected=2 | +| db | stock_movements запись на sale-line f4ea65fd… | ✓ count=1, sum=-2 (expected sum=-2) | +| db | stock_movements запись на sale-line 288b2be1… | ✓ count=1, sum=-2 (expected sum=-2) | +| db | stock_movements.Type = RetailSale (2) для sale документа | ✓ types=2 | + +## Summary + +- Passed: 12 +- Failed: 0 +- Warnings: 0 +- Skipped: 0 + +## Critical bugs + +Нет.