Files
ERP_Distribution/Distribution/docs/02_stock_ledger.md
T

3.4 KiB

02 — Stock Ledger

Status: Not Started Depends On: 01 — Item Catalog


Goal

Introduce stock-per-location as a first-class, transaction-driven concept. Covers mainPlan.md §8 in full, plus the location-query half of §6 (getStockByLocation, putawayStock). Everything downstream — loading events, delivery/sale ride execution, reconciliation — reads or writes through this ledger.


Background / Context

Nothing in the current schema tracks quantity of an Item anywhere. checkStockAvailability (§1) and every warehouse function in §6 have no data source to query today. This step must land before 04_loading_event.md.


Data Model Changes

Stock Balance (per location)

A denormalized running balance per (ItemId, LocationId) pair, kept in sync by the movement transaction table below — never edited directly by any service.

Stock Movement Transaction

One row per stock change: ItemId, LocationId, Quantity (signed: +load/return, -sale/damage), MovementType (Load, Sale, Return, Damage, Adjustment), ReferenceId (the order/ride/GRN that caused it), CreatedAt. This is the source of truth; the balance is its running sum.

Location Abstraction (Warehouse vs Vehicle)

A Location concept that can resolve to either a Warehouse or a Vehicle (once a ride is open — see 03_ride_lifecycle.md), so the same movement/balance model serves both getMultiLocationStock (§8) and the vehicle-as-temporary-warehouse pattern.


API Endpoints

Function reference Endpoint intent
getMultiLocationStock(sku) (§8) GET /api/stock/items/{sku} — balance across every location
getStockByLocation(warehouseId, sku) (§6) GET /api/stock/locations/{locationId}/items/{sku}
setReorderPoint(sku, warehouseId, threshold) (§8) PUT /api/stock/locations/{locationId}/items/{sku}/reorder-point
createStockTransfer(fromWarehouseId, toWarehouseId, items[]) (§8) POST /api/stock/transfers
trackBatchExpiry(sku, batchNo, expiryDate) (§8) Deferred — see Open Questions
calculateStockValuation(warehouseId, method) (§8) GET /api/stock/locations/{locationId}/valuation?method=fifo|weighted-average

Business Rules

  • Stock is never edited directly — every change is a Stock Movement Transaction; the balance is always the running sum, recalculated or cached but never hand-edited.
  • A movement cannot take a balance negative unless explicitly flagged as an Adjustment (physical count correction).
  • Every movement must carry a ReferenceId back to the business event that caused it — no freestanding movements outside of documented Adjustments.

Fraud / Control Considerations

  • Adjustment movements should be restricted to Supervisor level and above (see role-permission mapping in Data/Seed/RoleAndPermissionSeed.cs), and should require a reason code once 09_fraud_controls.md lands.

Open Questions

  • Is batch/expiry tracking (trackBatchExpiry) needed in v1, or can it be deferred until a regulated/perishable product line requires FEFO enforcement?
  • FIFO vs weighted-average valuation — does the business need both, or a single fixed method?

Implementation Notes

Fill this section in during planning discussion before writing any code.