5.6 KiB
5.6 KiB
Спринт 2 — складские документы (P1)
Автономная работа. После каждого пункта: dotnet build (SDK 8.0.126),
unit + integration + (где применимо) E2E тесты этого пункта, коммит порцией,
отметка [x] здесь, коммит прогресса.
Multi-tenant: все новые сущности — TenantEntity с OrganizationId +
query filter. Stock-инвариант: после каждого Post/Unpost
Stock.Quantity ≡ Σ StockMovement для (Product, Store).
Чек-лист
- P1-1 Оприходование (Enter) — Domain
Enter+EnterLine, EF, миграция, контроллер CRUD + Post/Unpost (Stock + StockMovement типEnter), Web/inventory/enters. Без поставщика (источник — начальные остатки, излишек инвентаризации). ✅ Контроллерapi/inventory/enters; миграцияPhase6a_Enters; пункт «Оприходования» в сайдбаре Admin/Storekeeper. Тесты: 4 интеграционных (post raise stock, unpost откатывает, double post→409, tenant-изоляция, блокировка unpost при минусе). - P1-2 Списание (Loss) — Domain
Loss+LossLine+ enumLossReason(Defect/Expired/Damage/Shortage/Other). EF, миграция, контроллер, Web,StockMovementтипWriteOff. ✅ Контроллерapi/inventory/losses(CRUD + Post/Unpost) с проверкой «не списать сверх остатка» (409). МиграцияPhase6b_Losses. Web с фильтром по причине и колонкой stockAtStore. Тесты: 3 интеграционных (post снижает stock, over-write-off → 409, tenant-изоляция). - P1-3 Перемещение (Transfer) — Domain
Transfer+TransferLine(FromStoreId → ToStoreId, обязательны и различны). Атомарная транзакция:TransferOutиз From +TransferInв To. EF, миграция, контроллер + Post/Unpost, Web. Кейс: post→unpost не оставляет orphan-движений. ✅ Пара движений (Out + In) в одной Serializable-транзакции; обратная пара в Unpost. Проверка «not short» на FromStore при Post и ToStore при Unpost. PermissionTransferEdit. Тесты: 4 интеграционных, ключевой проверяет что движений ровно 2 после Post и ровно 4 после Unpost (никаких orphan). - P1-4 Инвентаризация (Inventory) — Domain
Inventory+InventoryLine(productId, bookQty, actualQty, diff). EF, миграция. Контроллер: создание подгружает текущие остатки; Post создаётInventoryAdjustmentна diff. Web: форма со списком товаров склада, импорт CSV факта. ✅ Доменная сущностьInventoryDoc(имя чтобы не пересекаться с системным неймспейсом). Create с пустыми lines подтягивает все товары склада; Update пишет actualQty построчно. Post создаётInventoryAdjustmentтолько по строкам с diff != 0 (400 если нет расхождений). Unpost блочит при «излишек уже расходован». Web с CSV-импортом (productId|article;qty). Тесты: 3 интеграционных. - P1-6 Возврат от покупателя (CustomerReturn) — расширение
RetailSaleопцией возврата (referenceSaleId или без). Контроллер: создание возврата из проведённой продажи,CustomerReturnтип уже есть. Web: кнопка «Создать возврат». ✅ RetailSale.IsReturn + ReferenceSaleId; RetailSaleLine.QtyReturned (агрегация для защиты от over-return).POST /create-returnкопирует проведённый чек в Draft-возврат с qty = (Quantity - QtyReturned). Post return черезCustomerReturn-движение с +Quantity, инкрементит QtyReturned на исходных строках. Запрещён unpost оригинала при активных возвратах. Тесты: 3 интеграционных. - P1-7 Возврат поставщику (SupplierReturn) — по аналогии для Supply.
Domain
SupplierReturn+Line(referenceSupplyId). Контроллер. Web. ✅ Зеркалит Supply, но Post с -Quantity (типSupplierReturn). Валидация что reference указывает на проведённую приёмку того же поставщика. Защита от ухода в минус. Permissions переиспользуютSuppliesEdit/Post/Delete. Тесты: 4 интеграционных. - P1-16 Hangfire dashboard + cleanup —
Hangfire.Dashboardс авторизацией только для SuperAdmin. Scheduled: ежедневный cleanupStockMovementстарше 2 лет, audit-log старше 90 дней.
Лог
- Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса.
- Все правки на ветке
main(origin Forgejo), без коммитаglobal.json.