From a7b82eea86e7627bab15323434459660974abb42 Mon Sep 17 00:00:00 2001 From: nns Date: Thu, 28 May 2026 10:07:53 +0500 Subject: [PATCH] =?UTF-8?q?docs(sprint2):=20P1-16=20done=20=E2=80=94=20?= =?UTF-8?q?=D0=B2=D1=81=D0=B5=207=20=D0=BF=D1=83=D0=BD=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?,=20=D0=B8=D1=82=D0=BE=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- docs/sprint2-progress.md | 55 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/sprint2-progress.md b/docs/sprint2-progress.md index 7f27fd1..7aa5b7c 100644 --- a/docs/sprint2-progress.md +++ b/docs/sprint2-progress.md @@ -56,9 +56,62 @@ query filter. Stock-инвариант: после каждого Post/Unpost что reference указывает на проведённую приёмку того же поставщика. Защита от ухода в минус. Permissions переиспользуют `SuppliesEdit/Post/Delete`. Тесты: 4 интеграционных. -7. [ ] **P1-16 Hangfire dashboard + cleanup** — `Hangfire.Dashboard` с +7. [x] **P1-16 Hangfire dashboard + cleanup** — `Hangfire.Dashboard` с авторизацией только для SuperAdmin. Scheduled: ежедневный cleanup `StockMovement` старше 2 лет, audit-log старше 90 дней. + ✅ `Hangfire.PostgreSql` storage на ConnectionStrings:Default. Сервер + стартует только если `Hangfire:Enabled=true` (по умолчанию). Dashboard + `/hangfire` гейтит `SuperAdminHangfireFilter`. Recurring: `prune-stock-movements` + (03:30 UTC, 730 дней) и `prune-audit-log` (03:45 UTC, 90 дней) — + `HousekeepingJobs` с `IgnoreQueryFilters` (межтенантно). Тесты: + 1 unit + 1 интеграционный. + +## Итог + +**Все 7 пунктов выполнены.** Спринт 2 (складские документы P1) завершён 2026-05-28. + +Сводка: +- **P1-1 Enter** — оприходование без поставщика (`/api/inventory/enters`, миграция + `Phase6a_Enters`); 4 интеграционных теста. +- **P1-2 Loss** — списание с enum `LossReason` (`/api/inventory/losses`, + `Phase6b_Losses`); 3 интеграционных. +- **P1-3 Transfer** — атомарное перемещение пара TransferOut + TransferIn + (`/api/inventory/transfers`, `Phase6c_Transfers`); 4 интеграционных, включая + проверку «после Post ровно 2 движения, после Unpost ровно 4». +- **P1-4 Inventory** — пересчёт с auto-load остатков (`/api/inventory/inventories`, + `Phase6d_Inventories`); 3 интеграционных, импорт CSV в UI. +- **P1-6 CustomerReturn** — `RetailSale.IsReturn` + `ReferenceSaleId` + + `RetailSaleLine.QtyReturned`, эндпоинт `POST /create-return` + (`Phase6e_RetailSaleReturns`); 3 интеграционных. +- **P1-7 SupplierReturn** — зеркало Supply (`/api/purchases/supplier-returns`, + `Phase6f_SupplierReturns`); 4 интеграционных, валидация совпадения поставщика + при ссылке на приёмку. +- **P1-16 Hangfire** — `Hangfire.PostgreSql` storage, dashboard `/hangfire` + с `SuperAdminHangfireFilter`, recurring jobs `prune-stock-movements` (730 дней) + и `prune-audit-log` (90 дней); 1 unit + 1 интеграционный. + +**Сборка:** зелёная (`dotnet build src/food-market.api`). +**Тесты:** 24 unit + 32 integration = **56 зелёных**. +**Web:** `pnpm build` зелёный (5 новых пар list+edit страниц + расширение RetailSale). + +### Новые таблицы +`enters`, `enter_lines`, `losses`, `loss_lines`, `transfers`, `transfer_lines`, +`inventories`, `inventory_lines`, `supplier_returns`, `supplier_return_lines` ++ колонки `retail_sales.IsReturn/ReferenceSaleId`, `retail_sale_lines.QtyReturned`. + +### Новые permissions +`TransferEdit` добавлен в `RolePermissions` (Enter/Loss/Inventory/Supplies* — +переиспользованы существующие). `All()` обновлён. + +### Stock-инвариант +Каждый документ при Post создаёт явные `StockMovement` через `IStockService` +в Serializable-транзакции. Post→Unpost — обратные движения тем же документ-id +(reversal-маркер в `DocumentType`). Проверка «не уйти в минус» на: +- Enter Unpost (товар уже мог быть продан), +- Loss Post (нельзя списать сверх остатка), +- Transfer Post (FromStore) и Unpost (ToStore), +- Inventory Unpost (излишек мог уйти), +- SupplierReturn Post (нельзя вернуть сверх остатка). ## Лог