PRD: Stock, items & movement tracking
| Module | Inventory (CORE-06) | PRD ID | PRD-STK-001 |
| Status | Shipped | Owner | Inventory squad |
| Date | 2026-05-28 | Version | v1.0 |
| Packages | @nx/inventory · @nx/core · apps/client | URD | STK · ITM · TRK |
TL;DR
Gives a merchant a single, editable view of what they actually hold - on-hand, reserved, and available per item per location, with decimal precision - backed by an aggregate stock-list endpoint and an immutable movement audit trail. Low-stock thresholds and an attention signal surface items that need restocking, so an owner can see and correct stock at a glance instead of inferring it from the movement layer.
1. Context & Problem
Inventory already auto-creates an item per product variant (ITM) and logs every movement immutably (TRK), but a merchant has no consolidated, editable surface for what they actually hold. Stock figures live close to the movement layer, and the oversell flag sits on the item rather than on the stock record - so a clean "how much is on-hand / reserved / available per location" view, and a list endpoint to power it, are missing.
Without that surface a merchant cannot answer the most basic inventory question at a glance, cannot spot items running low, and cannot correct a count without reasoning about raw movement entries. This PRD closes that gap on top of the existing item and tracking primitives, giving the owner a first-class stock-management screen.
2. Goals & Non-Goals
Goals
- Surface on-hand / reserved / available per item per location with decimal precision (
STK). - First movement auto-creates the stock record; the oversell flag moves from item → stock record.
- An aggregate inventory-items list endpoint with stock rollup, plus an overview endpoint.
- Carry low-stock thresholds (min/max) through the list and flag items needing attention.
- A client stock screen: list, full-field edit panel, per-location editing, confirm-on-save, attention highlight.
- Keep the immutable movement audit trail (
TRK) as the source of truth behind every stock change.
Non-Goals
- Negative-stock enforcement at POS - negative available is tolerated (URD-CON-003).
- Multi-currency stock valuation.
- A dedicated historical stock-level browsing UI - history is recorded via
TRK;URD-STK-007remains a Could. - Inventory tickets and purchase-order receipt flows (TKT, PO) - separate increments.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Stock visibility | Owner can read on-hand / reserved / available per item per location from one screen |
| Audit integrity | Zero stock rows without a matching immutable movement record |
| Available accuracy | available = on-hand − reserved holds across the list at decimal precision |
| Attention signal | Items below their low-stock threshold are flagged and highlighted in the list |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Owner | See true stock per location, spot low stock, correct counts |
| Inventory staff | Edit stock fields and per-location quantities with a confirm step |
| Cashier | Read product availability at the point of sale |
Core scenarios: open the stock screen → see on-hand/reserved/available per item per location with low-stock items highlighted → edit a stock record (full fields, per-location) → confirm on save → the edit writes a movement record and the available figure recomputes.
5. User Stories
- As an owner, I want to see on-hand, reserved, and available per item per location on one screen, so I always know what I hold and where.
- As an owner, I want items below their threshold flagged and highlighted, so I can restock before I run out.
- As inventory staff, I want to edit a stock record's full fields per location, so I can correct counts accurately.
- As inventory staff, I want a confirm-on-save step on stock edits, so I don't commit a mistaken count.
- As an owner, I want every stock change backed by an immutable movement record, so the figure is always auditable.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | One stock record per (item, location, lot, serial); available = on-hand − reserved | URD-STK-001..002 |
| FR-2 | All quantities stored at decimal precision (4 places) | URD-STK-003 |
| FR-3 | First movement auto-creates the stock record; owner can view current levels | URD-STK-004 |
| FR-4 | Last-stocked / last-counted timestamps update automatically | URD-STK-005 |
| FR-5 | Stock updates are atomic - partial failure rolls back fully | URD-STK-006 |
| FR-6 | Oversell flag lives on the inventory-stock record (moved from the item) | URD-STK-001..004 |
| FR-7 | Aggregate inventory-items list endpoint with stock rollup, plus an overview endpoint | URD-STK-004 · URD-ITM-001..006 |
| FR-8 | Low-stock thresholds (min/max, min ≤ max) carried through the list and flag attention | URD-ITM-003 |
| FR-9 | Items are auto-created per variant, linked to the merchant's default location | URD-ITM-001..006 |
| FR-10 | Every stock change writes an immutable tracking entry (before/change/after, reference, idempotent) | URD-TRK-001..007 |
Full requirement text and acceptance criteria live in the Inventory URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | A stock change and its movement record are written together - no stock row without a matching immutable audit entry |
| Immutability | Movement records are append-only; corrections happen via new entries, never edits |
| Tenancy & authz | All operations scoped per merchant (x-merchant-id); gated by inventory permissions |
| Precision | Quantity math uses float(value, 4) |
| Consistency | Stock updates are atomic; partial failure rolls back fully |
| Performance | Aggregate list reads stock rollup + thresholds in one call, not N per-item lookups |
| i18n | User-facing labels/statuses are bilingual ({ en, vi }) |
8. UX & Flows
Key screens (in apps/client): the stock list and items table, the full-field stock edit panel with per-location editing, the confirm-on-save dialog, and the attention/highlight indicators for low stock.
9. Data & Domain
| Entity | Role |
|---|---|
InventoryStock | The quantity record - on-hand, reserved, available per (item, location, lot, serial); carries the oversell flag |
InventoryItem | The stock-tracked record per variant, linked to the default location; carries min/max thresholds |
InventoryTracking | Immutable movement entry (before/change/after, reference type/ID, tracking type) behind every change |
Conceptual only - full schema and invariants in the inventory domain model.
10. Dependencies & Assumptions
Depends on
- Inventory items (URD-ITM) - stock attaches to an auto-created item per variant.
- Movement audit trail (URD-TRK) - every stock change writes an immutable entry.
- Inventory locations (URD-LOC) - stock is held per location; a merchant default must exist.
@nx/core- item model and schema (thresholds, oversell flag location).
Assumptions
- Each merchant has a default inventory location and at least one product variant (hence an item).
- The movement (
TRK) layer is already in place as the audit source of truth.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Stock edit and movement record could diverge on partial failure | Updates are atomic; no stock row without a matching movement entry |
| Negative available tolerated | Documented business constraint (URD-CON-003), not a bug; POS enforcement out of scope |
| Default low-stock threshold value | Resolved - sensible default applied; threshold input validated min ≤ max |
| No dedicated stock-history viewer | History recorded via TRK; URD-STK-007 remains a Could, dedicated UI deferred |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 (foundation) - STK, ITM, TRK in the URD feature catalog |
| Rollout | All merchants; no feature flag |
| Migration | Oversell flag relocated item → stock record; thresholds added on the item model |
| Launch criteria | List shows on-hand/reserved/available per location; edit writes a movement record; available = on-hand − reserved holds; low-stock flagged |
| Monitoring | Stock-vs-movement consistency checks, low-stock counts per merchant, stock-edit error rate |
13. FAQ
Where does the oversell flag live now? On the inventory-stock record, not the item - so oversell is decided per stock row, alongside the quantities it governs.
What makes an item show as "needs attention"? Available falling below the item's low-stock threshold (min) flags it and highlights the row in the list.
Can I browse historical stock levels? Not via a dedicated viewer in this increment - every change is recorded immutably via TRK; a history UI (URD-STK-007) is a Could, deferred.
Is negative available a bug? No - negative available is a documented, tolerated business constraint (URD-CON-003); POS-level negative-stock enforcement is out of scope.
Does editing stock skip the audit trail? No - every stock change, including a manual edit, writes an immutable movement record.
References
- URD: Inventory - STK · ITM · TRK
- Related PRD: Opening Balance Import · Purchase Orders
- Module: Inventory - overview + traceability
- Developer: @nx/inventory · inventory-stock · domain model