food-market/tests/e2e/reports/full-cycle-2026-05-08-bugs-fixed.md
nns 42645174e0 e2e: bugs-fixed отчёт — все 12 шагов зелёные после fix HIGH+MEDIUM+2 gap'а
Прогон против 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 закрыты.
2026-05-08 12:16:17 +05:00

171 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
Нет.