26 KiB
10 · BACKEND — Phase 1 Spec (Inventory & Supply Chain)
Authoritative for: backend architecture, business rules, and the data model (the 38-entity schema). Navigation: you arrived here from
00-CORE.md. API request/response contracts are in11-BACKEND-PHASE1.md. Frontend rules are in20-FRONTEND.md. Record work inBackend/PROGRESS.md. Scope basis: SRS v1.1. Costing = FIFO · Multi-warehouse · Single-tenant · RBAC deferred (user identity stamped) · approvals auto/config-gated · vendor invoice + 3-way match deferred to Accounting.
Part A — Architecture & Layer Rules
The high-level layering is introduced in 00-CORE.md §4; this part is the authoritative detail the implementation must follow.
A.1 Layered flow (one direction only)
HTTP ─► Controller ─► Service ─► Repository ─► UnitOfWork / ErpDbContext ─► PostgreSQL
(DTOs) (logic, (entities) (transaction boundary)
returns DTOs)
Controller
- HTTP concerns only: routing, model binding, status codes,
[ProducesResponseType]. - Accepts and returns DTOs only. Never references EF entities or
ErpDbContext. - No business logic, no data access.
Service
- Owns business logic and orchestration. Returns DTOs.
- Maps entity ⇄ DTO (manual mapping is fine and dependency-free; keep mapping in the service or a dedicated mapper class).
- Opens the UnitOfWork transaction for any operation that spans more than one write.
Repository
- EF Core data access over entities. One repository per aggregate (Item, PurchaseOrder, Grn, Stock…).
- Query + persistence only. No business rules. Returns entities or projections to services.
UnitOfWork
- The transaction boundary. Wraps a single
ErpDbContext; exposesSaveChangesAsync()and an explicit transaction scope (BeginTransactionAsync) for multi-step stock operations. - Every stock-affecting operation (GRN confirm, transfer dispatch/receive, adjustment, purchase return, count post) runs inside one UoW transaction so it commits or rolls back atomically (NFR-02, NFR-05).
A.2 FIFO placement (critical)
- FIFO cost-layer consumption and valuation live in a domain service:
Services/Stock/FifoCostingService. - It is invoked by stock services inside the UoW transaction — never from a controller or repository.
- On any issue (transfer-out, adjustment-out, purchase return, future goods issue), it consumes open layers oldest-first, decrements
qtyRemaining, and returns the costed movement for the ledger. - Layer rows being consumed must be locked for the duration of the transaction to stay concurrency-safe (NFR-02). Use
SELECT … FOR UPDATEsemantics (EF: query within the transaction with appropriate locking) so two concurrent issues can't consume the same remaining quantity.
A.3 DTO boundary
- Every request body and response is a DTO in
Dtos/. Group by module (Dtos/Items,Dtos/Procurement,Dtos/Grn,Dtos/Stock,Dtos/Common). Dtos/Commonholds shared shapes:PagedResult<T>,PaginationMeta, and the error/problem shapes.- Entities in
Domain/Entitiesnever leave the service layer.
A.4 Cross-cutting
- Errors: RFC 7807
ProblemDetails(framework default). Domain exceptions inSystem/Errorscarry a stablecode; a middleware maps them toProblemDetails. Catalog in11-BACKEND-PHASE1.md §7. - Audit actor: an
ICurrentUserabstraction (Infra/Auth) resolves the acting user from the JWTsub. Services stamp mutations with it. Never trust acreatedByfrom the request body. - Concurrency: mutable resources carry a
RowVersion([Timestamp] byte[]), surfaced asETag;PUT/PATCHrequireIf-Match→412on mismatch. - Numbering: document numbers come from
NumberSequence(per doc type, per year), issued inside the same transaction as the document.
A.5 DI registration (lifetimes)
ErpDbContext: scoped (default).IUnitOfWork, repositories, services,ICurrentUser,FifoCostingService: scoped.- Register in
Program.cs(or anAddApplication()extension) afterAddDbContext.
Part B — Software Requirements Specification (v1.1)
Basis: ISO/IEC/IEEE 29148. Requirement IDs
FR-<AREA>-<n>; priority M/S/C (Must/Should/Could).
B.1 Introduction
B.1.1 Purpose
Specifies Phase 1 — the Inventory & Supply Chain subsystem: master data, procurement, goods receipt, stock and warehouse management, plus the integration seams reserved for later phases.
B.1.2 Scope
In scope: master data (Item, UOM & conversions, Category, Vendor, Warehouse/Bin); procurement (Requisition → RFQ → PO with approval + amendments → Purchase Return); GRN with inspection hold and partial receipt; stock management (count, transfer with in-transit, adjustment, ledger, FIFO valuation, reorder alerts); warehouse management (multi-warehouse, bin/location, batch/expiry and serial, basic putaway/pick); cross-cutting services (user identity + audit, document numbering, reason codes).
Out of scope (deferred, hooks retained): vendor invoice & three-way match → Accounting (GRN retains PO ref, received qty, received value per line); GL posting (Phase 1 emits GL-ready journal entries as data, no ledger); sales orders / reservation fulfilment / manufacturing consumption / formal QC dispositions (interfaces stubbed, §B.7); landed-cost apportionment (decision §B.1.2.1).
B.1.2.1 Landed cost decision (open)
Imports carry freight + duty + VAT, so true unit cost ≠ PO price.
- (A) Defer to Accounting — layers valued at PO price + directly-attributable line charges only. Simpler; valuation understates true cost.
- (B) Include apportionment at GRN — distribute additional charges across received lines into the FIFO layer cost. More accurate; one extra workflow.
Affects FR-GRN-06 and FR-STK-14. Recommendation: B if import duties are material; else A.
B.1.3 Definitions
GRN = Goods Receipt Note · PO = Purchase Order · RFQ = Request for Quotation · PR = Purchase Requisition · UOM = Unit of Measure · FIFO = First-In-First-Out costing · FEFO = First-Expiry-First-Out picking · Cost layer = quantity received at a specific unit cost, consumed FIFO · In-transit = stock left source, not yet confirmed at destination · ROP = reorder point · RBAC = role-based access control · GL = general ledger.
B.1.4 Overview
§B.2 overall description; §B.3 functional requirements; §B.4 data model; §B.5 external interfaces; §B.6 NFRs; §B.7 future seams; §B.8 appendices. The entity model is expanded in Part C.
B.2 Overall Description
B.2.1 Product perspective
Foundation of a modular ERP. Single-tenant, multi-warehouse. All later modules depend on the Item master, stock ledger, and vendor master defined here. Module boundaries are isolated behind services (Part A).
B.2.2 Product functions
Maintain master data; raise/approve procurement through PO and return; receive goods with inspection hold; track stock movements in a costed FIFO ledger across warehouses; perform counts/transfers/adjustments with audit; track batch/expiry/serial and locate by bin; raise reorder alerts.
B.2.3 User classes
Phase-1 note: role-based permissions are deferred (FR-X-01). Phase 1 runs a single operational user context — any user may perform any action, but each action is stamped with the authenticated user's identity for audit. The roles below are the functional blueprint for future RBAC, not enforced boundaries.
Storekeeper/Warehouse operator (receive, count, transfer, pick) · Procurement officer (requisitions, POs, vendors) · Approver/Manager (authorizes once approvals enabled) · Inventory controller (valuation, adjustments, reorder policy) · Auditor (read-only) · System administrator (users, numbering, config).
B.2.4 Operating environment
Web application; responsive UI incl. handheld/scanner. Relational DB with transactions + row-level locking (FIFO consumption needs it). Barcode/QR capable (designed-for).
B.2.5 Constraints
FIFO is a system-wide constraint; ledger must track cost layers. Multi-warehouse mandatory day 1; multi-tenancy out of scope. Single base currency (LKR) in Phase 1. Every stock transaction atomic + immutable ledger entry.
B.2.6 Assumptions & dependencies
One base currency; invoicing/3-way match in Accounting (GRN carries data); users/warehouses configured before transactions; landed-cost scope (§B.1.2.1) resolved before GRN valuation finalized.
B.3 Functional Requirements
B.3.1 Master Data (FR-MD)
| ID | Requirement | Pri |
|---|---|---|
| FR-MD-01 | Maintain Item master: SKU (unique), name, description, category, item type (Stocked/NonStocked/Service), tracking mode (None/Batch/Serial), status, tax class, default vendor. | M |
| FR-MD-02 | Maintain UOM master with base UOM per item and conversion factors (purchase→stock→base). | M |
| FR-MD-03 | Convert quantities between UOMs on every transaction; store base-UOM quantity in the ledger. | M |
| FR-MD-04 | Maintain hierarchical item categories. | S |
| FR-MD-05 | Hold reorder point and reorder quantity per item, optionally per warehouse. | M |
| FR-MD-06 | Maintain Vendor master: code, name, contact, terms, tax reg, status, currency. | M |
| FR-MD-07 | Maintain Warehouse master and, within each, a bin/location structure. | M |
| FR-MD-08 | Prevent deletion of any master referenced by a transaction; deactivate instead. | M |
B.3.2 Procurement (FR-PROC)
| ID | Requirement | Pri |
|---|---|---|
| FR-PROC-01 | Create Purchase Requisition with lines (item, qty, required-by, requester). | M |
| FR-PROC-02 | Optional RFQ: issue to vendors, record quotations for comparison. | S |
| FR-PROC-03 | Generate PO from PR/RFQ or directly (item, UOM, qty, price, tax, delivery date, warehouse). | M |
| FR-PROC-04 | [Phase 1: auto-approve] Auto-approve PO on creation (status Approved). Config flag approvalRequired (default off) gates a future approval workflow (authorization matrix); when on, PO cannot issue until approved. PendingApproval state + approval fields retained in schema (no migration to enable). |
M |
| FR-PROC-05 | [Phase 1: Option B — edit-while-open] PO may be freely edited while open (not fully received/closed); changes take effect immediately with an audit entry. Versioned amendments deferred; schema must not preclude adding a version field later. | S |
| FR-PROC-06 | PO lifecycle: Draft → (PendingApproval →) Approved → PartiallyReceived → FullyReceived → Closed/Cancelled. Phase 1 bypasses PendingApproval via auto-approve. | M |
| FR-PROC-07 | Support partial receipt; PO stays open until fully received or manually closed. | M |
| FR-PROC-08 | Support Purchase Return referencing original GRN/PO line; generates outbound movement. | M |
| FR-PROC-09 | Retain data sufficient for future three-way match without schema change. | M |
B.3.3 Goods Receipt (FR-GRN)
| ID | Requirement | Pri |
|---|---|---|
| FR-GRN-01 | Create GRN against an approved PO, defaulting lines/quantities from open PO lines. | M |
| FR-GRN-02 | Support GRN without PO (direct/emergency) by permission, flagged for review. | S |
| FR-GRN-03 | Support over/under-receipt tolerances (per item or global); warn or block beyond tolerance. | S |
| FR-GRN-04 | Capture batch + expiry and/or serial numbers for tracked items on receipt. | M |
| FR-GRN-05 | Allow receipt into inspection/quarantine hold (not issuable) pending QC, before QC module exists. | M |
| FR-GRN-06 | On confirm, each line creates a FIFO cost layer at unit cost (PO price + attributable charges; landed cost per §B.1.2.1) and posts an inbound ledger entry. | M |
| FR-GRN-07 | Record received value per line and PO reference for downstream matching. | M |
| FR-GRN-08 | Assign received stock to a bin/location (putaway). | S |
B.3.4 Stock Management (FR-STK)
| ID | Requirement | Pri |
|---|---|---|
| FR-STK-01 | Maintain an immutable, append-only stock ledger: item, warehouse, bin, batch/serial, qty (base UOM), unit cost, value, running balance, source doc, user, timestamp. | M |
| FR-STK-02 | Maintain FIFO cost layers per item per warehouse (received qty, remaining qty, unit cost, receipt date). | M |
| FR-STK-03 | On any issue, consume oldest layers first, posting cost at each layer's unit cost. | M |
| FR-STK-04 | Compute valuation = Σ(remaining qty × unit cost) over open layers, per item/warehouse and total. | M |
| FR-STK-05 | Support transfer between warehouses/bins with in-transit: out consumes source layers; in confirms and creates destination layer. | M |
| FR-STK-06 | Transfers are cost-preserving: destination layer inherits consumed source cost. | M |
| FR-STK-07 | [Phase 1: auto-post] Adjustments (increase/decrease/write-off) with mandatory reason code post immediately; a config flag (default off) gates threshold approval later. Reason code + user stamp always mandatory. | M |
| FR-STK-08 | Support cycle count and full physical count workflows; post variance adjustments on confirmation. | M |
| FR-STK-09 | Define negative-stock policy (default: block issues that would drive on-hand negative; configurable per item). | M |
| FR-STK-10 | Provide reorder alerts when available ≤ reorder point; optionally suggest a requisition. | M |
| FR-STK-11 | Expose a reservation/allocation status on stock (stubbed Phase 1; consumed by Sales later) to distinguish on-hand vs available. | S |
| FR-STK-12 | Stock enquiry by item/warehouse/bin/batch/serial showing on-hand, in-transit, on-hold, available. | M |
| FR-STK-13 | Every stock transaction generates a GL-ready journal entry as data (no posting in Phase 1). | S |
| FR-STK-14 | With landed cost enabled (Option B), apportion additional charges into FIFO layer costs at GRN by configurable basis. | C |
B.3.5 Warehouse Management (FR-WH)
| ID | Requirement | Pri |
|---|---|---|
| FR-WH-01 | Support multiple warehouses, each with a bin/location hierarchy. | M |
| FR-WH-02 | Track stock to bin level; support bin-to-bin movement within a warehouse. | M |
| FR-WH-03 | Support batch/lot tracking with expiry; enforce FEFO picking for perishables (distinct from FIFO costing). | M |
| FR-WH-04 | Support serial-number tracking across lifecycle (receipt → issue). | M |
| FR-WH-05 | Provide basic putaway and pick steps. | S |
| FR-WH-06 | Be barcode/QR-scan ready for item, bin, batch, serial. | C |
| FR-WH-07 | Block issue/pick of on-hold/quarantine or expired stock. | M |
B.3.6 Cross-cutting (FR-X)
| ID | Requirement | Pri |
|---|---|---|
| FR-X-01 | [Phase 1: user identity only] Authenticate users and stamp every transaction with the acting user's identity for audit. Full RBAC (role→permission matrix, from which approvals derive) is deferred; reserve role/permission structures for no-migration enablement. | M |
| FR-X-02 | Maintain an immutable audit trail for every create/update/delete and stock movement (who/when/old→new/reason). | M |
| FR-X-03 | Generate document numbers from configurable sequences (per type, per year), unique and gap-controlled. | M |
| FR-X-04 | Maintain configurable reason-code lists (adjustments, returns, count variances). | M |
| FR-X-05 | No editing/deleting confirmed/posted transactions; corrections via reversing entries. | M |
B.4 Data Model (summary)
Key entities and relationships are enumerated in Part C. The commitment: Item, UOM/UOMConversion, Category, Vendor, Warehouse/Bin, ItemReorder, Requisition(+Line), RFQ(+Line)/VendorQuotation, PurchaseOrder(+Line), GRN(+Line), PurchaseReturn(+Line), StockLayer (FIFO), StockLedger (immutable), Batch, Serial, StockTransfer(+Line), StockAdjustment(+Line), StockCount(+Line), User, ReasonCode, NumberSequence, AuditLog, JournalEntryStub, and reserved RBAC (Role, Permission, UserRole, RolePermission).
B.5 External Interfaces
UI: responsive; count/pick screens handheld-friendly; status badges; mandatory-field validation. Hardware: barcode/QR (designed-for). Software: relational DB with transactional integrity + row locking; internal service interfaces/events for Phase-2+ modules.
B.6 Non-Functional Requirements
| ID | Category | Requirement |
|---|---|---|
| NFR-01 | Performance | Single item/warehouse enquiry + valuation < 2s under normal load; ledger posting transactional, < 1s per line. |
| NFR-02 | Integrity | FIFO layer consumption atomic and concurrency-safe; no double-consumption of remaining qty. |
| NFR-03 | Security | Users authenticated; passwords hashed; every action attributed to a user and logged. (Role-based enforcement deferred, FR-X-01.) |
| NFR-04 | Auditability | Audit trail immutable, retained per policy; ledger append-only. |
| NFR-05 | Reliability | No stock transaction partially commits; full rollback on failure. |
| NFR-06 | Scalability | Growth in items/warehouses/ledger without redesign; ledger indexed for time-series queries. |
| NFR-07 | Usability | Receive/count/transfer achievable with minimal training; scan-first where hardware present. |
| NFR-08 | Maintainability | Modular service boundaries; Phase-2+ integrates without altering Phase-1 schema. |
| NFR-09 | Availability | Not a Phase-1 engineering commitment. Uptime SLA to be set for production; HA infra (failover, redundancy) deferred to a later hardening phase. |
| NFR-10 | Configurability | Numbering, tolerances, reason codes, negative-stock and reorder policies configurable without code change. |
B.7 Future Modules & Integration Seams
| Module | Seam in Phase 1 |
|---|---|
| Accounting | GL-ready journal entries per movement (FR-STK-13); GRN retains PO ref + received value for 3-way match (FR-PROC-09, FR-GRN-07). |
| Sales & CRM | Reservation/allocation status distinguishing on-hand vs available (FR-STK-11). |
| Manufacturing | Generic goods-issue/consumption movement type BOM will consume through (extends FR-STK-03). |
| QC / QA | GRN inspection/quarantine hold (FR-GRN-05); hold blocks issue (FR-WH-07). |
| HRM | User identity foundation (FR-X-01) reusable for employee-linked permissions. |
| RBAC & Approvals | Config flags + retained PendingApproval/role structures enable PO & adjustment approvals with no schema change. |
B.8 Appendices
B.8.1 Status lifecycles PO: Draft → (PendingApproval →) Approved → PartiallyReceived → FullyReceived → Closed/Cancelled · GRN: Draft → Confirmed → Closed · Transfer: Draft → InTransit → Received → Closed · Adjustment/Count: Draft → (PendingApproval →) Posted.
B.8.2 Document numbering (examples)
PR-YYYY-#####, PO-YYYY-#####, GRN-YYYY-#####, TRF-YYYY-#####, ADJ-YYYY-#####, CNT-YYYY-#####, PRET-YYYY-##### (FR-X-03).
B.8.3 Reason codes (seed) Adjustment: Damage, Theft/Loss, Count Variance, Expiry Write-off, System Correction · Return: Defective, Wrong Item, Over-supply, Quality Reject.
B.8.4 Open decisions log
| # | Decision | Status |
|---|---|---|
| 1 | Landed-cost scope (§B.1.2.1, A vs B) | Open |
| 2 | Negative-stock: global block vs per-item (FR-STK-09) | Proposed: block |
| 3 | GRN-without-PO permission scope (FR-GRN-02) | Open |
| 4 | Availability/uptime SLA target (NFR-09) | Deferred to production |
| 5 | PO & adjustment approvals | Resolved: deferred — auto-approve, config-gated off |
| 6 | RBAC | Resolved: deferred — user identity only |
| 7 | PO amendments | Resolved: Option B, edit-while-open |
| 8 | Costing method | Resolved: FIFO |
| 9 | Tenancy | Resolved: single-tenant |
Part C — ER Model (38 entities)
Costing: FIFO · Multi-warehouse · Single-tenant. Legend: PK primary key · FK foreign key. Companion visual diagrams (Mermaid / draw.io ERD) accompany this repo; this part is the authoritative textual model.
C.1 Master Data
CATEGORY(category_id PK, parent_id FK→CATEGORY, name)
UOM(uom_id PK, name)
UOM_CONVERSION(conversion_id PK, item_id FK→ITEM, from_uom FK→UOM, to_uom FK→UOM, factor)
ITEM(item_id PK, sku, name, category_id FK→CATEGORY, base_uom_id FK→UOM,
default_vendor_id FK→VENDOR, item_type, tracking_mode, tax_class, status)
ITEM_REORDER(reorder_id PK, item_id FK→ITEM, warehouse_id FK→WAREHOUSE, reorder_point, reorder_qty)
VENDOR(vendor_id PK, code, name, terms, tax_reg, currency, status)
WAREHOUSE(warehouse_id PK, code, name)
BIN(bin_id PK, warehouse_id FK→WAREHOUSE, code, bin_type)
C.2 Procurement
REQUISITION(requisition_id PK, doc_no, requested_by FK→USER, status, created_at)
REQUISITION_LINE(req_line_id PK, requisition_id FK→REQUISITION, item_id FK→ITEM, qty, required_by)
RFQ(rfq_id PK, doc_no, requisition_id FK→REQUISITION, status)
RFQ_LINE(rfq_line_id PK, rfq_id FK→RFQ, item_id FK→ITEM, qty)
VENDOR_QUOTATION(quotation_id PK, rfq_id FK→RFQ, vendor_id FK→VENDOR, unit_price, lead_days)
PURCHASE_ORDER(po_id PK, doc_no, vendor_id FK→VENDOR, requisition_id FK→REQUISITION,
status, approval_required, created_by FK→USER, created_at)
PO_LINE(po_line_id PK, po_id FK→PURCHASE_ORDER, item_id FK→ITEM, uom_id FK→UOM,
warehouse_id FK→WAREHOUSE, qty, unit_price, tax, qty_received)
PURCHASE_RETURN(return_id PK, doc_no, vendor_id FK→VENDOR, warehouse_id FK→WAREHOUSE,
reason_code_id FK→REASON_CODE, created_by FK→USER)
PURCHASE_RETURN_LINE(return_line_id PK, return_id FK→PURCHASE_RETURN,
grn_line_id FK→GRN_LINE, item_id FK→ITEM, qty)
C.3 Goods Receipt
GRN(grn_id PK, doc_no, po_id FK→PURCHASE_ORDER, vendor_id FK→VENDOR,
warehouse_id FK→WAREHOUSE, status, created_by FK→USER, created_at)
GRN_LINE(grn_line_id PK, grn_id FK→GRN, po_line_id FK→PO_LINE, item_id FK→ITEM, uom_id FK→UOM,
bin_id FK→BIN, batch_id FK→BATCH, qty, unit_cost, received_value, hold_status)
C.4 Batch / Serial
BATCH(batch_id PK, item_id FK→ITEM, batch_no, expiry_date)
SERIAL(serial_id PK, item_id FK→ITEM, serial_no, status)
C.5 Stock Core (FIFO + Ledger)
STOCK_LAYER(layer_id PK, item_id FK→ITEM, warehouse_id FK→WAREHOUSE, batch_id FK→BATCH,
serial_id FK→SERIAL, grn_line_id FK→GRN_LINE,
qty_received, qty_remaining, unit_cost, receipt_date) -- FIFO layers
STOCK_LEDGER(ledger_id PK, item_id FK→ITEM, warehouse_id FK→WAREHOUSE, bin_id FK→BIN,
batch_id FK→BATCH, serial_id FK→SERIAL, user_id FK→USER,
direction, qty_base, unit_cost, value, running_balance,
source_doc_type, source_doc_id, created_at) -- immutable journal
C.6 Stock Transactions
STOCK_TRANSFER(transfer_id PK, doc_no, src_warehouse_id FK→WAREHOUSE,
dest_warehouse_id FK→WAREHOUSE, status, created_by FK→USER)
STOCK_TRANSFER_LINE(transfer_line_id PK, transfer_id FK→STOCK_TRANSFER, item_id FK→ITEM,
src_bin_id FK→BIN, dest_bin_id FK→BIN, batch_id FK→BATCH, serial_id FK→SERIAL, qty)
STOCK_ADJUSTMENT(adjustment_id PK, doc_no, warehouse_id FK→WAREHOUSE,
reason_code_id FK→REASON_CODE, created_by FK→USER, created_at)
STOCK_ADJUSTMENT_LINE(adj_line_id PK, adjustment_id FK→STOCK_ADJUSTMENT, item_id FK→ITEM,
bin_id FK→BIN, batch_id FK→BATCH, serial_id FK→SERIAL, qty_delta)
STOCK_COUNT(count_id PK, doc_no, warehouse_id FK→WAREHOUSE, count_type, status, created_by FK→USER)
STOCK_COUNT_LINE(count_line_id PK, count_id FK→STOCK_COUNT, item_id FK→ITEM, bin_id FK→BIN,
system_qty, counted_qty, variance)
C.7 Cross-cutting
USER(user_id PK, username, display_name, status)
REASON_CODE(reason_code_id PK, code, description, context)
NUMBER_SEQUENCE(sequence_id PK, doc_type, year, last_number)
AUDIT_LOG(audit_id PK, user_id FK→USER, entity_type, entity_id, action, change_set, created_at)
JOURNAL_ENTRY_STUB(journal_id PK, source_doc_type, source_doc_id, debit_account, credit_account, amount)
C.8 Reserved (RBAC — deferred, schema placeholder only)
ROLE(role_id PK, name)
PERMISSION(permission_id PK, code)
USER_ROLE(user_id FK→USER, role_id FK→ROLE)
ROLE_PERMISSION(role_id FK→ROLE, permission_id FK→PERMISSION)
C.9 Modeling notes (load-bearing)
- FIFO = two structures.
STOCK_LAYERanswers valuation ("what's on hand and at what cost");STOCK_LEDGERanswers history ("what moved, when, by whom"). Layers are keyed per item per warehouse. - Polymorphic source.
STOCK_LEDGER.source_doc_type/source_doc_id(andAUDIT_LOG,JOURNAL_ENTRY_STUB) reference the originating document without a hard FK per type — new transaction types (Sales, Manufacturing) write to the ledger without a schema change. - In-transit + cost-preserving transfer.
STOCK_TRANSFERholdssrc/destwarehouse; dispatch consumes source layers into in-transit, receive creates the destination layer at the inherited source cost. - FEFO ≠ FIFO. FIFO governs costing; FEFO governs physical picking of perishables via
BATCH.expiry_date. - Reserved RBAC. Role/Permission/UserRole/RolePermission exist for schema-completeness only; only
USERis live (audit stamp). - Reorder alerts are a query, not an entity — computed from
ITEM_REORDERvs available. Add a table only if alert history is required.
C.10 Entity → implementation mapping
- Entities →
Domain/Entities; enums (ItemType,TrackingMode,HoldStatus,Direction,*Status,CountType) →Domain/Enums. - EF configurations (
IEntityTypeConfiguration<T>, one per entity) →Infra/Persistence/Configurations. - FIFO logic →
Services/Stock/FifoCostingService(Part A.2). Ledger writes only via stock services inside the UoW transaction. RowVersion(concurrency) on mutable aggregates: Item, Vendor, PurchaseOrder, GRN, transfers/adjustments/counts headers.
End of 10-BACKEND-PHASE1.md. API contracts: 11-BACKEND-PHASE1.md. Record work: Backend/PROGRESS.md.