7.2 KiB
Backend — PROGRESS (Phase 1: Inventory & Supply Chain)
Legend: [ ] not started · [~] in progress · [x] done
Spec: docs/10-BACKEND-PHASE1.md (model + rules) · docs/11-BACKEND-PHASE1.md (API)
Convention: docs/01-DOC-GUIDE.md §6. Update this file in the same commit as the code. When ticking [x], append a short note + any deviation.
0. Bootstrap
- Solution + Web API project (
net10.0), packages restored (00-CORE §5.4) - Folder structure per 00-CORE §5.3
ErpDbContext+ Npgsql wired;InitialCreatemigration created and applied (2026-07-10, 8 master-data tables)./health→Healthy.- Serilog, JWT, Swagger, HealthChecks, ProblemDetails in
Program.cs(JWT bearer validated; endpoints not yet[Authorize]-gated — see §6 auth note) IUnitOfWork+UnitOfWork(transaction boundary)- Generic repository base + interfaces
ICurrentUser(audit stamp from tokensub)- ProblemDetails middleware + domain exception →
codemapping (System/Errors; full §7 catalog added toErrorCodes)
1. Master Data
Code complete for all items below (2026-07-09). Live smoke test PASSED against Postgres (2026-07-10): create/get/list/update/status/reorder/uom-conversions across all 5 controllers; ETag round-trip 200 / stale→412 / missing→428; SKU_DUPLICATE→400; bad reference→422; missing-field→400 ValidationProblemDetails; category
?tree=truenesting;pageSize=9999clamped to 200; deactivate via PATCH status→204. Still[~](not[x]) for one reason: the security gate (00-CORE §8) — the foundational auth control (02-SECURITY B.1) and the audit trail (B.3, the AR-01 compensating control) land in §6. Flip to[x]once §6 auth+audit are wired.
- Item: entity + config + enums (ItemType, TrackingMode; EntityStatus added) —
xmin/RowVersion concurrency token (Npgsql), unique SKU - [~] Item: repository (generic) + service + controller (CRUD, narrow DTOs, ETag/If-Match→412, SKU_DUPLICATE, reference validation) — server-controlled fields excluded (02-SECURITY C.1)
- [~] UOM + UOM conversions (
GET/POST /uoms,PUT /items/{id}/uom-conversionsfull-replace upsert) - [~] Category (hierarchy,
GET /categories?tree=truenested build, parent-exists validation) - [~] Vendor (CRUD, ETag/If-Match, unique code, deactivate via
PATCH /vendors/{id}/status) - [~] Warehouse + Bin (
/warehouses, nested/warehouses/{id}/bins, bin code unique per warehouse) - [~] Item reorder settings (
PUT /items/{id}/reorderfull-replace upsert, warehouse-exists validation)
2. Procurement
- Requisition (+ lines) + submit
- RFQ + quotations + comparison
- Purchase Order: create (auto-approve,
approvalRequiredflag), edit-while-open, approve (no-op), cancel - Purchase Return (outbound movement, reason code)
3. Goods Receipt
- GRN create (against PO / direct), over-receipt tolerance
- GRN confirm → FIFO layer + ledger + PO
qtyReceived(single UoW txn, Idempotency-Key) - Inspection hold release / reject
4. Stock Core
- StockLayer + StockLedger entities/config (ledger append-only)
FifoCostingService(consume oldest-first with row lock; valuation)- Stock enquiry (onHand / available / onHold / inTransit)
- Ledger query · Valuation query
5. Stock Transactions
- Transfer: create → dispatch (consume, In-Transit) → receive (dest layer, cost-preserving)
- Adjustment (auto-post, mandatory reason code)
- Count (cycle/full → enter counts → variance → post)
- Reorder alerts (query) + suggest requisition
6. Cross-cutting
Auth-enforcement gap (open): JWT bearer validation is wired, but no token issuer exists yet and controllers are not
[Authorize]-gated, so §1 endpoints are currently open. This is the AR-01/NFR-03 control surface — gate all v1 endpoints (fallback authorization policy) in the same change asPOST /auth/login, then re-run the 02-SECURITY B.1 checklist and flip §1 items to[x].
- Audit log on every mutation (who/when/old→new)
- Document numbering sequences (per type, per year)
- Auth: simple in-app login → JWT (
POST /auth/login) - JournalEntryStub emitted per stock movement (data only)
- Negative-stock policy enforcement (default block)
- FEFO picking for perishables; block expired / on-hold issue
Deferred (Phase 2+ — do NOT build now, hooks only)
- Vendor invoice + three-way match
- Reservation/allocation fulfilment
- RBAC policy enforcement + approval workflow activation
Done
2026-07-09 — Bootstrap verified + Master Data (§1) implemented
- Bootstrap scaffolding confirmed against 00-CORE §5 (solution, packages,
Program.cswiring, UoW, generic repo,ICurrentUser, ProblemDetails handler). Added enum-as-string JSON (JsonStringEnumConverter) and registered the 5 master-data services. - Domain: 3 enums (
ItemType,TrackingMode,EntityStatus) + 8 entities (Category, Uom, UomConversion, Item, ItemReorder, Vendor, Warehouse, Bin) with oneIEntityTypeConfigurationeach; FKsRestrict(masters deactivate, not cascade-delete), unique indexes (SKU, vendor/warehouse code, uom name, bin code per-warehouse), decimal precision,xminconcurrency token on Item/Vendor. - API: 5 controllers, lowercase routes matching
docs/11 §2exactly (verified via generatedswagger.json). ETag/If-Match (428 if missing, 412 on mismatch), narrow request DTOs (no over-posting),PagedResponse<T>list envelope (§1.4),PageQuerywith pageSize clamp ≤200 (B.6). - Migration
InitialCreategenerated (xmincorrectly produces no DDL — uses the PG system column). - Verified:
dotnet buildclean (0 warn/0 err); app boots (Now listening… Application started);/api/meta200;swagger.json200 with all 13 master-data paths; DI resolves controller→service→repo→DbContext (a DB-backed call reaches Npgsql, failing only on creds). - Blocked / follow-ups: (1) auth enforcement + audit trail — §6 (the remaining security gate for
[x]); (2) noDELETEmaster endpoints —MASTER_IN_USEcode reserved until transaction tables exist (deactivate-only per FR-MD-08); (3) minor: bad-enum bind error leaks the CLR type name indetail(02-SECURITY B.5) — fine in Dev, tidy before prod.
2026-07-10 — Migration applied + live smoke test PASSED
dotnet ef database updateappliedInitialCreateto local Postgres;/health→Healthy.- End-to-end curl smoke across all 5 controllers — all green: warehouse/bin create+list; uom create; category + child +
?tree=truenesting; vendor create + PUT (If-Match 200 / stale 412 / missing 428); item create (201, referencing category/uom/vendor) + GET (ETag header) + list/filterq+pageSize=9999→clamped 200; reorder PUT; uom-conversions PUT; full item PUT with fresh ETag→200; PATCH status Inactive→204; duplicate SKU→400SKU_DUPLICATE; bad reference→422; missing required→400 ValidationProblemDetails; bad enum→400. Concurrency token (xmin) confirmed incrementing per mutation. - Note: local dev DB now holds smoke-test rows (warehouse/bin/uom×2/category×2/vendor/item, item left Inactive). Reset any time with
dotnet ef database drop -f && dotnet ef database update.