first commit
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
# 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 in `11-BACKEND-PHASE1.md`. Frontend rules are in `20-FRONTEND.md`. Record work in `Backend/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`; exposes `SaveChangesAsync()` 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 UPDATE` semantics (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/Common` holds shared shapes: `PagedResult<T>`, `PaginationMeta`, and the error/problem shapes.
|
||||
- Entities in `Domain/Entities` never leave the service layer.
|
||||
|
||||
## A.4 Cross-cutting
|
||||
- **Errors:** RFC 7807 `ProblemDetails` (framework default). Domain exceptions in `System/Errors` carry a stable `code`; a middleware maps them to `ProblemDetails`. Catalog in `11-BACKEND-PHASE1.md §7`.
|
||||
- **Audit actor:** an `ICurrentUser` abstraction (`Infra/Auth`) resolves the acting user from the JWT `sub`. Services stamp mutations with it. **Never** trust a `createdBy` from the request body.
|
||||
- **Concurrency:** mutable resources carry a `RowVersion` (`[Timestamp] byte[]`), surfaced as `ETag`; `PUT`/`PATCH` require `If-Match` → `412` on 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 an `AddApplication()` extension) after `AddDbContext`.
|
||||
|
||||
---
|
||||
|
||||
# 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_LAYER` answers valuation ("what's on hand and at what cost"); `STOCK_LEDGER` answers history ("what moved, when, by whom"). Layers are keyed per item **per warehouse**.
|
||||
- **Polymorphic source.** `STOCK_LEDGER.source_doc_type/source_doc_id` (and `AUDIT_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_TRANSFER` holds `src`/`dest` warehouse; 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 `USER` is live (audit stamp).
|
||||
- **Reorder alerts are a query**, not an entity — computed from `ITEM_REORDER` vs 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`.*
|
||||
Reference in New Issue
Block a user