Skip to content

PRD: Inventory Tickets

ModuleInventory (CORE-06)PRD IDPRD-TKT-001
StatusIn-progressOwnerInventory squad
Date2026-05-26Versionv0.1
Packages@nx/core · @nx/inventoryURDTKT

TL;DR

Gives a merchant one document - the Inventory Ticket - to capture, route, and execute every warehouse operation: transfer between locations, stock adjustment, cycle count, returns, and scrap. Each ticket carries line items and moves through a shared approval lifecycle so a planned movement is only applied to stock once it is approved, with the eventual movement fully traceable to the ticket that authorized it.

1. Context & Problem

Warehouse operations - transferring stock between locations, adjusting quantities, cycle counting, returning goods to a vendor or from a customer, scrapping spoiled or damaged inventory - all need a document that captures intent, moves through approval, and only touches stock at the very end. Inventory already has locations, items, stock, and an immutable tracking trail, but no operation document tying a planned movement to its eventual tracking rows.

Without such a document, every non-purchase stock change is ad-hoc: there is no controlled state between "intended" and "applied", no approval gate, and no single record an owner can point to and say "this is why stock moved". The Inventory Ticket aggregate is that document. It is modelled to cover the full operation space up front - multiple ticket types, one generic lifecycle, polymorphic partner/origin references, and a return/backorder self-link - so later milestones add execution behavior without further schema migration.

2. Goals & Non-Goals

Goals

  • A single InventoryTicket header spanning all stock-operation types: Transfer, Adjustment, Cycle Count, Return to Vendor, Return from Customer, Scrap/Disposal, plus Purchase Request, Stock In, and Internal Use.
  • A shared lifecycle DRAFT → SUBMITTED → APPROVED → IN_PROGRESS → COMPLETED / CANCELLED, expressed as typed constants with transition guards (canSubmit / canApprove / canStart / canComplete / canCancel).
  • Line items (InventoryTicketItem) with planned vs. actual quantities, persisted with merchant-scoped, soft-deleted CRUD alongside the header.
  • A schema that reserves the structural columns for downstream execution (polymorphic partner ref, polymorphic origin ref, return/backorder self-link, spawned-PO link, effective-date backdating, reason code) so no later migration is required to add behavior.

Non-Goals

  • Stock-effecting lifecycle execution - completing a ticket to create InventoryTracking rows and update stock (URD-TKT-004) is deferred to a later milestone; the CRUD service does not yet implement the transitions.
  • Multi-line aggregate write routes (header + items in one call) - deferred with the lifecycle.
  • Backorder partial-execution splits - the self-link column is reserved, with no behavior in this increment.
  • Quotation / quota-based reservation and negative-stock enforcement at POS - out of module scope per the URD.

3. Success Metrics

MetricTarget / signal
Operation coverageOne ticket header models 100% of the planned stock-operation types - no per-type document needed
Schema durabilityDownstream execution lands with zero additional schema migration (columns reserved up front)
Tenancy isolationEvery ticket and line is merchant-scoped; cross-merchant reads return nothing
Lifecycle correctnessEvery state change passes a transition guard; no illegal transition is persisted

4. Personas & Use Cases

PersonaGoal in this feature
Inventory staffDraft a ticket for a warehouse operation, attach its lines, and route it for approval
Owner / ManagerReview and approve tickets before they affect stock; cancel mistaken ones
System (later milestone)Apply an approved ticket to stock and write the audit trail on completion

Core scenarios: create a ticket of a given operation type → add its line items (planned quantities) → submit → approve → start → complete (stock execution deferred to a later milestone) - or cancel from any non-terminal state.

5. User Stories

  • As inventory staff, I want to create a ticket of a specific operation type (transfer, adjustment, count, return, scrap), so a planned warehouse movement has one home.
  • As inventory staff, I want to attach line items with planned quantities, so the ticket states exactly what should move.
  • As inventory staff, I want to submit a ticket for approval, so stock is not touched until someone signs off.
  • As an owner, I want to approve or cancel a ticket through guarded transitions, so only valid lifecycle moves are allowed.
  • As an owner, I want every ticket and its lines isolated to my merchant, so another merchant can never see or alter them.
  • As the platform, I want the schema to already carry partner, origin, return-link, spawned-PO and reason-code columns, so later execution behavior needs no migration.

6. Functional Requirements

#RequirementURD ref
FR-1InventoryTicket header models the full operation space - 9 types (PURCHASE_REQUEST, STOCK_IN, TRANSFER, INTERNAL_USE, ADJUSTMENT, CYCLE_COUNT, RETURN_TO_VENDOR, RETURN_FROM_CUSTOMER, SCRAP_DISPOSAL), a superset of the 6 the URD enumeratesURD-TKT-001
FR-2Shared lifecycle DRAFT → SUBMITTED → APPROVED → IN_PROGRESS → COMPLETED / CANCELLED, as typed statuses with ACTIVE_SET/TERMINAL_SET and canSubmit/canApprove/canStart/canComplete/canCancel guardsURD-TKT-002
FR-3Line items (InventoryTicketItem) carry planned vs. actual quantities, persisted with the headerURD-TKT-003
FR-4Completing a ticket creates tracking entries and updates stockURD-TKT-004
FR-5Line items reserve lot/serial columns at the schema layerURD-TKT-005
FR-6Per-item location override - header carries source/destination, lines split per binURD-TKT-006
FR-7Header reserves a polymorphic partner reference (Vendor / Customer) and a polymorphic origin reference for downstream linkageURD-TKT-001
FR-8Header reserves a return/backorder self-link, a spawned-PO link, a reason code, and an effective date for backdatingURD-TKT-006
FR-9Header and lines are merchant-scoped with soft-delete; CRUD honors merchant isolationURD-TKT-002

Full requirement text and acceptance criteria live in the Inventory URD. This PRD references them rather than restating them. FR-4 (stock-effecting completion) and the multi-line aggregate write route are scheduled for a later milestone - see §2 Non-Goals and §12.

7. Non-Functional Requirements

AreaRequirement
Data integrityWhen stock execution lands, the stock change and its tracking entry are written together - no stock change without a matching immutable audit entry
Tenancy & authzAll operations scoped per merchant (x-merchant-id); gated by inventory permissions
Lifecycle safetyState transitions only via guard predicates; terminal states (COMPLETED / CANCELLED) are immutable
Schema durabilityStructural columns reserved up front so downstream execution needs no migration
Soft-deleteHeader and lines use soft-delete; soft-deleted records are excluded from standard listings
PrecisionPlanned/actual quantity math uses decimal precision (float(value, 4))
i18nUser-facing type/status/partner labels are bilingual ({ en, vi })

8. UX & Flows

Key screens (in apps/client): ticket list, ticket create/edit by operation type, line-item editor, and the lifecycle status control. The ticket controllers and route definitions live in @nx/inventory (controllers/inventory-ticket{,-item}).

9. Data & Domain

EntityRole
InventoryTicketThe operation document - ITI_… identifier, type, status, source/destination location, polymorphic partner, polymorphic origin, return/backorder self-link, spawned-PO link, reason code, effective date, lifecycle timestamps, merchant scope
InventoryTicketItemA ticket line - item reference with planned vs. actual quantities; reserves lot/serial and per-bin location columns
InventoryTicketTypesThe 9 operation types, grouped Procurement / Routine / Returns / End-of-life; PURCHASE_REQUEST is workflow-only (no stock effect, spawns a PO)
InventoryTicketStatusesTyped lifecycle statuses + ACTIVE_SET/TERMINAL_SET/isTerminal and the transition guards
InventoryTicketPartnerTypesVendor / Customer with i18n labels for the partner discrimination

Conceptual only - full schema and invariants in the inventory domain model.

10. Dependencies & Assumptions

Depends on

  • Inventory locations (URD-LOC) - tickets reference a source/destination location.
  • Stock levels & movement audit (URD-STK · URD-TRK) - the deferred completion step will read stock and write tracking rows.
  • Vendors / Customers (URD-VEN) - the polymorphic partner reference points at a vendor or customer.
  • Purchase Orders (URD-PO) - a PURCHASE_REQUEST ticket spawns a PO via the reserved spawned-PO link.

Assumptions

  • The merchant has at least one inventory location for source/destination references.
  • Stock-effecting completion lands in a later milestone before the feature is end-to-end usable.

11. Risks & Open Questions

Risk / questionMitigation / status
Lifecycle execution not yet implemented - service is pure CRUDTracked as a Non-Goal this increment; transition handlers and aggregate route scheduled for a later milestone
Schema breadth (9 types, polymorphic refs) without behavior could drift from real needsColumns reserved deliberately to avoid migration; behavior validated when execution lands
Backorder partial-execution semantics undefinedSelf-link column reserved; split behavior is an open design question for the execution milestone
Reverting a completed ticket vs. already-written trackingOpen: define the reversal/compensation path once completion writes stock

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 (multi-location operations) - see URD feature catalog
RolloutAll merchants; no feature flag
MigrationNew entities (InventoryTicket, InventoryTicketItem) added with full column set; no later migration planned for execution
Launch criteriaThis increment: header + line schema and merchant-scoped CRUD verified. Feature-complete (later milestone): submit→approve→start→complete transitions execute and a COMPLETED ticket writes tracking + updates stock
MonitoringTicket volume per merchant by type, transition-guard rejections, and (post-execution) stock-vs-tracking consistency checks

13. FAQ

Does completing a ticket move stock today? Not yet - this increment ships the header/line schema and merchant-scoped CRUD. The stock-effecting completion (URD-TKT-004) is scheduled for a later milestone.

Why 9 ticket types when the URD lists 6? The schema models the full operation space up front (adding Purchase Request, Stock In, and Internal Use) so later milestones add execution without a schema migration.

What is PURCHASE_REQUEST for? It is a workflow-only type - it has no direct stock effect and instead spawns a purchase order via the reserved spawned-PO link.

Can one merchant see another's tickets? No - every ticket and line is merchant-scoped (x-merchant-id) with soft-delete; cross-merchant reads return nothing.

How are partial/return deliveries handled? The header carries a return/backorder self-link column, reserved now; the partial-execution split behavior is defined when lifecycle execution lands.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.