URD: Inventory
| Module | CORE-06 | Version | v0.6 |
|---|---|---|---|
| Status | In-progress | Date | 2026-06-15 |
Business documentation. This URD is Inventory's feature list - each feature below is one Functional Area (
<AREA>). The same<AREA>keys the feature's PRDs (PRD-<AREA>-NNN) and tests (TC-<AREA>-NNN), and each feature is listed in the Delivery feature catalog. See the Feature Spine convention.
1. Purpose
Define user-facing requirements for Inventory management - stock tracking, warehouse locations, purchase orders, vendors, materials (BOM), inventory tickets, and a complete, immutable audit trail of every stock movement. The outcome is that a merchant always knows what they have, where it is, and how it got there.
2. Scope
| Included | Excluded |
|---|---|
| Inventory locations (warehouses) with hierarchy | Sale quotation / quota-based reservation |
| Inventory items (variant/material linked to location) | Multi-level BOM / sub-assemblies |
| Stock quantities (on-hand, reserved, available) | Recipe yield and scrap percentage |
| Immutable tracking audit log | Multi-currency stock valuation |
| Tracking type catalog (19 pre-seeded) | Full barcode scanning integration |
| Vendor (supplier) management | Negative-stock enforcement at POS |
| Purchase orders with goods receipt | Technical API specifications |
| Inventory tickets (transfer, adjustment, count, return, scrap) | |
| Material catalog with identifiers | |
| Material recipes (BOM) with versioning | |
| BOM runtime explosion on kitchen ticket | |
| Production orders, inventory rules, unit of measure |
3. Definitions
| Term | Definition |
|---|---|
| Inventory Location | A warehouse or storage area within a merchant. Supports hierarchy. One default per merchant. |
| Inventory Item | A record linking a product variant or material to a merchant for stock tracking. |
| Inventory Stock | The quantity record - on-hand, reserved, available. One per (item, location, lot, serial). |
| Inventory Tracking | An immutable audit entry for every stock movement with before/after quantities. |
| Inventory Tracking Type | Categorizes movement direction (IN/OUT/NEUTRAL) and reason. 19 pre-seeded + 1 custom. |
| Inventory Ticket | A document for stock operations: transfer, adjustment, cycle count, returns, scrap. Has line items. |
| Vendor | A supplier from whom the merchant purchases goods. |
| Purchase Order | A goods-receipt document from a vendor, containing line items with quantities and pricing. |
| Material | A raw ingredient or semi-finished good tracked separately from sellable products. |
| Material Recipe (BOM) | A versioned recipe declaring which materials are consumed to produce a product variant. |
| BOM Explosion | Automatic material decrement triggered by kitchen ticket item status changes. |
| Production Order | A manufacturing document tracking planned/actual/scrap quantities for produced goods. |
| Inventory Rule | A configurable threshold rule that can trigger alerts, PO creation, or webhooks. |
| Unit of Measure | Measurement unit with 3-level scope: system defaults → merchant → product/material. |
4. Conceptual Model
Conceptual only - the full schema lives in the developer domain model.
5. Feature Catalog
The feature list of this module. Each row is one feature (a Functional Area). Detail in §6. Mirrored in the Delivery feature catalog.
| Feature ID | Feature | Phase | Status | Priority |
|---|---|---|---|---|
LOC | Inventory Locations | P1 | Built | High |
ITM | Inventory Items | P1 | Built | High |
STK | Stock Levels | P1 | Built | High |
TRK | Movement Audit Trail | P1 | Built | High |
LSE | Lot / Serial & Expiry Identifiers | P3 | Built | High |
TKT | Inventory Tickets | P2 | Built | High |
VEN | Vendors | P1 | Built | High |
PO | Purchase Orders | P1 | Built | High |
POI | Purchase Order Items | P1 | Built | High |
MAT | Materials | P3 | Built | High |
REC | Recipes / BOM | P3 | In-progress | High |
PRO | Production Orders & BOM Explosion | P3 | In-progress | High |
IOP | Stock operations - F&B & Retail | P2 | Planned | High |
Status: live from Plane where mapped, otherwise registry-declared. Vocabulary mirrors Plane (state-group / phase).
6. Features
One sub-section per feature, in catalog order. Each feature keeps its description, requirements, and acceptance together. Priority = MoSCoW (Must / Should / Could / Won't).
LOC - Inventory Locations Built
Feature ID: inventory/LOC · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: merchants organize stock into warehouses/storage areas. Every merchant gets one default location automatically; larger merchants can add a hierarchy of locations with address and GPS data.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-LOC-001 | M | Owner can create an inventory location with an i18n name. |
| URD-LOC-002 | M | A default location is auto-created for each merchant on commerce init. |
| URD-LOC-003 | M | Exactly one default location exists per merchant; setting a new default clears the previous. |
| URD-LOC-004 | S | Locations support parent-child hierarchy. |
| URD-LOC-005 | S | A location can store address and geographic (GPS) data. |
| URD-LOC-006 | M | Location lifecycle: NEW → ACTIVATED → DEACTIVATED → ARCHIVED (ARCHIVED terminal). |
| URD-LOC-007 | M | Each location has a system-generated identifier (LOC_YYYYMMDD_id), not user-editable. |
| URD-LOC-008 | M | Locations use soft-delete; data is preserved and admin-retrievable. |
| URD-LOC-009 | S | Owner can list and filter locations by status. |
| URD-LOC-010 | M | Locations are isolated per merchant. |
Acceptance
AC-LOC-01: Default Location
| Given | When | Then |
|---|---|---|
| New merchant | Commerce initialization | Default location auto-created |
| Set a new default | - | Previous default cleared |
ITM - Inventory Items Built
Feature ID: inventory/ITM · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: a stock-tracked record is created automatically whenever a product variant is added, linked to the merchant's default location, with optional min/max thresholds.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-ITM-001 | M | An inventory item is auto-created when a product variant is created. |
| URD-ITM-002 | M | One item per (variant, location, unit) combination. |
| URD-ITM-003 | S | Item supports min/max stock-level thresholds; min ≤ max. |
| URD-ITM-004 | M | Each item has a system-generated identifier (SKU_YYYYMMDD_id), not user-editable. |
| URD-ITM-005 | M | Item lifecycle: ACTIVATED → DEACTIVATED → ARCHIVED (ARCHIVED terminal). |
| URD-ITM-006 | M | Item is linked to the merchant's default location on creation. |
Acceptance
AC-ITM-01: Auto-create on variant
| Given | When | Then |
|---|---|---|
| A new product variant | Variant is created | An inventory item is auto-created at the default location |
STK - Stock Levels Built
Feature ID: inventory/STK · Phase: P1 · PRDs: Opening Balance Import · Dev: @nx/inventory
What it does for users: merchants see how much stock they have - on-hand, reserved, and available - per item per location, with decimal precision and atomic updates.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-STK-001 | M | One stock record per (item, location, lot, serial). |
| URD-STK-002 | M | Available = on-hand − reserved. |
| URD-STK-003 | M | All quantities stored with decimal precision (4 places). |
| URD-STK-004 | M | Owner can view current stock levels; first movement auto-creates the stock record. |
| URD-STK-005 | S | Last-stocked / last-counted timestamps update automatically. |
| URD-STK-006 | M | Stock updates are atomic - partial failure rolls back fully. |
| URD-STK-007 | C | Historical stock-level changes are viewable. |
Acceptance
AC-STK-01: Stock Tracking
| Given | When | Then |
|---|---|---|
| Item with inventory | View | On-hand, reserved, available displayed |
| Available drops below threshold | - | Low-stock indicator shown |
TRK - Movement Audit Trail Built
Feature ID: inventory/TRK · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: every change in stock - receipt, sale, transfer, adjustment - is recorded as an immutable entry with before/change/after quantities, fully traceable to its source document.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-TRK-001 | M | Every stock movement creates an immutable tracking entry. |
| URD-TRK-002 | M | Records before, change, and after quantities. |
| URD-TRK-003 | M | Entries from business documents store reference type and ID. |
| URD-TRK-004 | M | Idempotent: same (referenceType, referenceId) produces one entry only. |
| URD-TRK-005 | M | Each entry typed by tracking type (IN/OUT/NEUTRAL); 19 pre-seeded + custom. |
| URD-TRK-006 | S | Entries record the acting user (createdBy / modifiedBy). |
| URD-TRK-007 | M | Owner can view full tracking history. |
| URD-TRK-008 | S | Entries can record effective price and multiplier. |
| URD-TRK-009 | S | Manual entries can include a note/reason. |
| URD-TRK-010 | C | Filter history by date, type, or reference document. |
Acceptance
AC-TRK-01: Immutable entry
| Given | When | Then |
|---|---|---|
| Any stock movement | It occurs | An immutable entry records before/change/after + reference |
| Same (referenceType, referenceId) replayed | - | No duplicate entry is created (idempotent) |
LSE - Lot / Serial & Expiry Identifiers Built
Feature ID: inventory/LSE · Phase: P3 · PRDs: Lot / serial & expiry identifiers · Dev: @nx/inventory
What it does for users: stock becomes traceable below the item. The same item at one location splits into separate buckets per lot and per serial, each carrying its own expiry and manufacture date, while an identifier registry turns a scanned code (SKU / barcode / QR at the item level, IMEI / serial at the unit level) into exactly one inventory entity.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-LSE-001 | M | Stock is bucketed by (item, location, lot, serial); distinct lots/serials of one item at one location are separate stock records. |
| URD-LSE-002 | M | Each stock bucket carries a lot number, serial number, expiry date, and manufacture date. |
| URD-LSE-003 | M | Buckets with no lot/serial collapse to one record (equal NULLs treated as the same bucket), so non-traceable items keep a single record. |
| URD-LSE-004 | M | Lot / serial / expiry are captured at goods receipt on purchase-order lines; a serial-tracked line records one serial per received unit. |
| URD-LSE-005 | M | Inventory-ticket lines capture lot / serial / expiry for stock-in and return-from-customer; a serial line must have planned quantity 1. |
| URD-LSE-006 | M | Every movement snapshots the lot / serial / expiry that moved into the immutable tracking entry. |
| URD-LSE-007 | M | An identifier registry records SKU / BARCODE / QRCODE at the item level and IMEI / SERIAL at the unit (stock) level. |
| URD-LSE-008 | M | Each (scheme, code) pair is globally unique across active records - one code resolves to exactly one inventory entity. |
| URD-LSE-009 | M | Identifier scheme defaults to UNKNOWN when unspecified; only SKU / BARCODE / QRCODE / IMEI / SERIAL are recognized. |
| URD-LSE-010 | M | Identifiers are merchant-scoped through their principal (item or stock) and use soft-delete; soft-deleting a code frees its (scheme, code) pair. |
| URD-LSE-011 | S | Expired stock is disposed through a dedicated EXPIRED movement reason that decrements the specific lot. |
| URD-LSE-012 | S | A production run can stamp its finished-good output with a lot number and expiry date for recall traceability. |
| URD-LSE-013 | C | Expiry and manufacture dates captured per lot support expiring-soon visibility and FEFO selection (data model only; operator UI lands with IOP). |
Acceptance
AC-LSE-01: Lot bucketing
| Given | When | Then |
|---|---|---|
| Two receipts of one item with different lot numbers | Received | Two distinct stock buckets exist, each with its own expiry |
| A non-traceable item (no lot/serial) | Received twice | One bucket accumulates - equal NULL lot/serial collapse |
AC-LSE-02: Unique identifier resolution
| Given | When | Then |
|---|---|---|
| A registered (scheme, code) | Scanned | It resolves to exactly one inventory entity |
| Same (scheme, code) on another record | Saved | Refused - the pair must be unique across active records |
| A registered code is soft-deleted | The pair is re-registered | Allowed - soft-delete frees the pair |
AC-LSE-03: Traceable movement & disposal
| Given | When | Then |
|---|---|---|
| Any movement of a traceable bucket | It occurs | The tracking entry snapshots the lot / serial / expiry that moved |
| An expired lot | Disposed | Decremented via the EXPIRED reason and recorded in the trail |
| A serial-tracked receipt line with quantity > 1 | Saved | Refused - a serial line must be exactly one unit |
TKT - Inventory Tickets Built
Feature ID: inventory/TKT · Phase: P2 · PRDs: - · Dev: @nx/inventory
What it does for users: warehouse operations run through tickets - transfer between locations, stock adjustment, cycle count, returns, and scrap - each moving through an approval workflow before it touches stock.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-TKT-001 | M | Tickets support 6 operation types: Transfer, Adjustment, Cycle Count, Return to Vendor, Return from Customer, Scrap/Disposal. |
| URD-TKT-002 | M | Lifecycle: DRAFT → SUBMITTED → APPROVED → IN_PROGRESS → COMPLETED / CANCELLED. |
| URD-TKT-003 | M | Each ticket has line items with planned vs. actual quantities. |
| URD-TKT-004 | M | Completing a ticket creates tracking entries and updates stock. |
| URD-TKT-005 | S | Line items support lot/serial numbers. |
| URD-TKT-006 | S | Line items support per-item location override. |
Acceptance
AC-TKT-01: Inventory Ticket
| Given | When | Then |
|---|---|---|
| Transfer ticket | COMPLETED | Source decreased, destination increased |
| Cycle count ticket | COMPLETED | Variance calculated and applied |
| Adjustment ticket | COMPLETED | Stock corrected with reason code |
VEN - Vendors Built
Feature ID: inventory/VEN · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: merchants keep a directory of suppliers - contact details, address, tax registration - referenced by purchase orders and preserved for history even after removal.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-VEN-001 | M | Owner can create vendors within a merchant. |
| URD-VEN-002 | M | Vendor has name, contact info (emails, phones), address, and a system identifier (VEN_YYYYMMDD_id). |
| URD-VEN-003 | S | Vendor can store a tax registration number. |
| URD-VEN-004 | M | Vendor uses soft-delete; historical PO references are preserved. |
| URD-VEN-005 | M | Vendors are isolated per merchant. |
Acceptance
AC-VEN-01: Vendor lifecycle
| Given | When | Then |
|---|---|---|
| Owner creates a vendor | Saved | Vendor gets a system identifier and is merchant-isolated |
| Vendor referenced by a PO is removed | Soft-delete | Historical PO references are preserved |
PO - Purchase Orders Built
Feature ID: inventory/PO · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: owners receive goods from vendors via a purchase-order workflow; goods receipt increments stock and writes the audit trail, with OVERRIDE and ACCUMULATIVE receipt modes.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-PO-001 | M | Owner can create a PO selecting vendor + items in one operation; vendor required. |
| URD-PO-002 | M | Lifecycle: DRAFT → PROCESSING → RECEIVED → COMPLETED → CLOSED (with valid transitions only). |
| URD-PO-003 | M | Owner can cancel a PO from any non-terminal status; received stock is not auto-reversed. |
| URD-PO-004 | M | Owner can revert PROCESSING → DRAFT. |
| URD-PO-005 | M | PO items are editable only in DRAFT status. |
| URD-PO-006 | M | A PO must have ≥1 item before it can be confirmed. |
| URD-PO-007 | M | Goods receipt increments stock and creates tracking entries. |
| URD-PO-008 | M | Receipt supports OVERRIDE and ACCUMULATIVE modes. |
| URD-PO-009 | M | PO defaults to the merchant's default location unless one is specified. |
| URD-PO-010 | M | PO total = subtotal − discount + tax. |
| URD-PO-011 | S | Manual vs. system discount/tax are flagged distinctly. |
| URD-PO-012 | M | Each PO has a system-generated purchase-order number. |
Acceptance
AC-PO-01: Purchase Order Receipt
| Given | When | Then |
|---|---|---|
| PO in PROCESSING | Goods received | Stock incremented, tracking created |
| OVERRIDE mode | - | Replaces received quantity |
| ACCUMULATIVE mode | - | Adds to received quantity |
POI - Purchase Order Items Built
Feature ID: inventory/POI · Phase: P1 · PRDs: - · Dev: @nx/inventory
What it does for users: each PO line computes its own total from price, quantity, multiplier, tax and discount; adding the same item+unit accumulates quantity, and zeroing a line removes it.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-POI-001 | M | Adding the same (itemType, itemId, uom) accumulates quantity; different uom creates a new line. |
| URD-POI-002 | M | Item total = (unitPrice × quantity × multiplier) + tax − discount. |
| URD-POI-003 | M | Setting an item quantity ≤ 0 soft-deletes the line and recalculates the PO total. |
| URD-POI-004 | S | Unit price defaults to the variant's active cost price. |
| URD-POI-005 | S | UOM defaults to the inventory item's base unit. |
Acceptance
AC-POI-01: Line accumulation
| Given | When | Then |
|---|---|---|
| Same (itemType, itemId, uom) added twice | Added | Quantity accumulates on one line |
| Line quantity set to 0 | Saved | Line soft-deleted and PO total recalculated |
MAT - Materials Built
Feature ID: inventory/MAT · Phase: P3 · PRDs: - · Dev: @nx/inventory
What it does for users: F&B merchants track raw ingredients separately from sellable products, each with i18n names and multiple identifier schemes (SKU, barcode, QR) and barcode search.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-MAT-001 | M | Owner can create materials within a merchant. |
| URD-MAT-002 | M | Material has a system identifier (MAT_YYYYMMDD_id) and an i18n name. |
| URD-MAT-003 | M | Material name supports multiple locales. |
| URD-MAT-004 | M | Material description is editable. |
| URD-MAT-005 | M | Material can be deactivated. |
| URD-MAT-006 | M | Material can be archived (terminal, read-only). |
| URD-MAT-007 | M | Material identifier is unique. |
| URD-MAT-101 | M | Owner can attach a BARCODE identifier to a material. |
| URD-MAT-102 | M | Material supports identifier schemes: SYSTEM, SLUG, SKU, BARCODE, QRCODE. |
| URD-MAT-103 | M | A (scheme, code) pair is unique across materials. |
| URD-MAT-104 | M | Identifier scheme defaults to SYSTEM when unspecified. |
| URD-MAT-105 | S | Multiple identifiers of different schemes can coexist on one material. |
| URD-MAT-106 | S | Material is searchable by registered barcode. |
Acceptance
AC-MAT-01: Barcode identifier
| Given | When | Then |
|---|---|---|
| Owner attaches a BARCODE to a material | Searched by that barcode | The material is found |
| Same (scheme, code) on another material | Saved | Rejected - pair must be unique |
REC - Recipes / BOM In-progress
Feature ID: inventory/REC · Phase: P3 · PRDs: - · Dev: @nx/inventory · ADR-0001
What it does for users: F&B merchants define versioned recipes (bill of materials) per product variant; when a kitchen ticket item progresses, the active recipe auto-decrements its materials and records the movement.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-REC-001 | M | Owner can create a recipe attached to a product variant. |
| URD-REC-002 | M | Recipe has a version (default 1.0). |
| URD-REC-003 | M | (principal, version) is unique per recipe. |
| URD-REC-004 | M | Recipe items specify a material, quantity, and unit. |
| URD-REC-005 | M | Recipe items can be added to a recipe. |
| URD-REC-006 | M | Same material appears at most once per recipe. |
| URD-REC-007 | M | Owner can activate a recipe. |
| URD-REC-008 | M | Owner can deactivate a recipe. |
| URD-REC-009 | S | Editing an activated recipe creates a new version; the original is preserved. |
| URD-REC-010 | S | Multiple recipe versions can be listed per principal. |
| URD-REC-011 | M | Only the activated recipe version is used at runtime. |
| URD-REC-101 | M | Recipe item references a valid material. |
| URD-REC-102 | M | Recipe item quantity stored at decimal precision (4 places). |
| URD-REC-103 | M | Recipe item stores a unit. |
| URD-REC-104 | M | Recipe item quantity is editable. |
| URD-REC-105 | M | Recipe item soft-delete allows re-adding the same material. |
| URD-REC-201 | M | A kitchen-ticket-item status change triggers recipe lookup and decrement prep at the default location. |
| URD-REC-202 | M | Each recipe component is decremented; a tracking entry (Used as Material) is created. |
| URD-REC-203 | M | Component decrement updates stock at the default location. |
| URD-REC-204 | M | The activated recipe for the variant is the one exploded. |
| URD-REC-205 | M | A material.stock-changed event is emitted after explosion. |
| URD-REC-206 | S | No default location → log warning, skip (no hard failure). |
| URD-REC-207 | S | No activated recipe → silent no-op. |
| URD-REC-208 | C | Non-default kitchen location is respected (pending - current code uses the merchant default). |
Acceptance
AC-REC-01: Kitchen BOM explosion
| Given | When | Then |
|---|---|---|
| Variant with activated recipe | Kitchen item status changes | Materials decremented per recipe item |
| No activated recipe | - | Silent no-op |
| No default location | - | Warning logged, skipped |
IOP - Stock Operations for F&B & Retail Planned
Feature ID: inventory/IOP · Phase: P2 (Jul-Aug) · PRDs: PRD-IOP-001 · Dev: @nx/inventory
What it does for users: merchants actually operate their stock from the app - vouchers, purchase orders, counts - then go industry-deep: F&B recipes auto-deduct ingredients with lot/expiry; retail runs in/out by barcode with live counter stock. (The backend largely exists; this increment makes it usable end-to-end.)
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-IOP-001 | M | Owner records goods-in / goods-out with voucher screens (all ticket types usable from the app) |
| URD-IOP-002 | M | Purchase orders managed end-to-end from the app (create → receive) |
| URD-IOP-003 | M | Stock count & adjustment performed from the app |
| URD-IOP-004 | M | F&B: selling a recipe product deducts its ingredients automatically (verified e2e, with recipe screens) |
| URD-IOP-005 | M | Retail: stock in/out by barcode scan |
| URD-IOP-006 | S | Lot + expiry captured at receipt; expiring-soon visibility |
| URD-IOP-007 | S | Real-time stock visible at the counter |
| URD-IOP-008 | C | Grocery lightweight repack (1 input item → 1 output item) |
Acceptance
AC-IOP-01: Operate stock from the app
| Given | When | Then |
|---|---|---|
| A merchant with vendors | Creates a PO, receives it, counts stock | Stock matches voucher history; expense voucher posted on receipt |
| An F&B dish with a recipe | A customer buys it | Ingredient stock decreases per the recipe |
PRO - Production Orders & BOM Explosion In-progress
Feature ID: inventory/PRO · Phase: P3 · PRDs: PRD-PRO-001 · Dev: @nx/inventory
What it does for users: make-to-stock merchants (bakeries, central kitchens, light assembly) plan a production run as a document - which finished good, from which recipe, how much, where - tracking planned vs. actual vs. scrap through a lifecycle. Behind it, a multi-level BOM explosion flattens the bound recipe through nested sub-assemblies down to the leaf materials a run consumes, with depth and cycle guards.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-PRO-001 | M | Owner can create a production order targeting a finished good (ProductVariant or Material) within the merchant. |
| URD-PRO-002 | M | A production order is bound to a material recipe (BOM) that drives component explosion. |
| URD-PRO-003 | M | Production order carries planned / actual / scrap quantities in its unit of measure at decimal precision (4 places). |
| URD-PRO-004 | M | Production order lifecycle: DRAFT → IN_PROGRESS → DONE / CANCELLED (DONE and CANCELLED terminal); only valid transitions allowed. |
| URD-PRO-005 | M | Each production order has a unique, system-generated production number, not user-editable. |
| URD-PRO-006 | M | Production order names the location where components are drawn and the finished good is produced. |
| URD-PRO-007 | S | Production order supports scheduling windows (scheduled start / end) for production-queue planning. |
| URD-PRO-008 | S | Production order records start / complete / cancel timestamps. |
| URD-PRO-009 | S | Production order can carry an output lot number and expiry date for recall traceability. |
| URD-PRO-010 | M | Production orders are merchant-isolated and use soft-delete. |
| URD-PRO-011 | M | A bound recipe is flattened across nested sub-assemblies down to leaf materials (multi-level BOM). |
| URD-PRO-012 | M | Explosion uses the latest ACTIVATED recipe version for each principal. |
| URD-PRO-013 | M | Explosion is bounded by a maximum nesting depth and rejects cyclic recipe chains. |
| URD-PRO-014 | M | Each leaf component quantity is the product of the quantities along its explosion path. |
| URD-PRO-015 | M | A product-variant component without an activated sub-recipe is skipped; a material without a sub-recipe is a leaf consumed directly. |
| URD-PRO-016 | M | Production consumption and finished-good output are recorded as immutable tracking entries referencing the production order, typed PRODUCTION_CONSUME / PRODUCTION_OUTPUT. |
Acceptance
AC-PRO-01: Create a production order
| Given | When | Then |
|---|---|---|
| A finished good with an activated recipe | Owner creates a production order with a planned quantity and location | A unique production number is assigned and the order opens in DRAFT |
| Same merchant | Order created | Order is merchant-isolated and soft-deletable |
AC-PRO-02: Lifecycle transitions
| Given | When | Then |
|---|---|---|
| Order in DRAFT | Started / cancelled | Moves to IN_PROGRESS / CANCELLED |
| Order in IN_PROGRESS | Completed / cancelled | Moves to DONE / CANCELLED |
| Order in DONE or CANCELLED | Any transition | Refused - terminal state |
AC-PRO-03: Multi-level BOM explosion
| Given | When | Then |
|---|---|---|
| A recipe whose component is itself a sub-assembly | Explosion runs | Flattened to leaf materials with quantities multiplied along each branch |
| A recipe chain that references itself | Explosion runs | Refused - cycle detected |
| A recipe tree deeper than the maximum | Explosion runs | Refused - maximum depth exceeded |
| A variant component with no activated sub-recipe | Explosion runs | Skipped, no leaf emitted |
AC-PRO-04: Production tracking vocabulary
| Given | When | Then |
|---|---|---|
| A production movement | Recorded | An immutable tracking entry typed PRODUCTION_CONSUME / PRODUCTION_OUTPUT references the production order |
7. Constraints & Non-Goals
Cross-cutting requirements (
CON) apply to all features above and are tested asTC-CON-*.
Cross-cutting requirements (CON)
| ID | P | Requirement |
|---|---|---|
| URD-CON-001 | M | All inventory endpoints require authentication (JWT or basic auth). |
| URD-CON-002 | M | All records use soft-delete; soft-deleted records are excluded from standard listings. |
| URD-CON-003 | M | Negative available quantity is currently tolerated (documented business constraint, not a bug). |
| URD-CON-004 | M | Stock deductions are idempotent per (referenceType, referenceId). |
Constraints
| ID | Constraint |
|---|---|
| C-01 | One default location per merchant at all times. |
| C-02 | One stock record per (item, location, lot, serial). |
| C-03 | Available = on-hand − reserved (negative currently tolerated). |
| C-04 | Tracking records are immutable. |
| C-05 | Stock movements are idempotent via (referenceType, referenceId). |
| C-06 | PO items editable only in DRAFT. |
| C-07 | 19 pre-seeded tracking types (+ 1 custom) at startup. |
| C-08 | All records use soft-delete. |
| C-09 | All quantities use decimal precision (4 places). |
Non-Goals
- Multi-level BOM (material as principal for sub-assemblies)
- Recipe yield and scrap percentage
- Explode-at-sale BOM for non-F&B retail
- Negative-stock enforcement at POS
- Full barcode scanning for stock takes
- Multi-currency stock valuation
8. Version History
| Date | Author | Description | Ver |
|---|---|---|---|
| 2026-02-28 | Q. Do - QE | Initial from code analysis | v0.1 |
| 2026-04-15 | Claude (AI pair) | Add Material and MaterialRecipe (BOM) requirements | v0.2 |
| 2026-04-16 | Q. Do - QE | Consolidated requirement areas | v0.3 |
| 2026-05-30 | Claude (AI pair) | Reformat to module template; reconcile tracking types to 19 (dev docs) | v0.4 |
| 2026-06-04 | Claude (AI pair) | Reorganize by feature (Feature Spine); each feature carries its own requirements + acceptance; CON moved to Constraints | v0.5 |
| 2026-06-15 | Claude (AI pair) | Add LSE - Lot / serial & expiry identifiers (traceable stock buckets + identifier registry) | v0.6 |