Skip to content

PRD: Sale order & cart

ModuleSalePRD IDPRD-ORD-001
StatusReady to devFEATORD
EpicPlaneBANA-1520
Date2025-12-31Versionv1.0
Packages@nx/saleURDORD
SurfaceSale · POS
OwnerPhát Nguyễn

TL;DR

Merchants build a sale as a draft cart, edit line items, then checkout - prices lock and the order enters a controlled lifecycle that drives it to PARTIAL or COMPLETED on payment. First end-to-end "cart → order → payment" spine of the Orders module; payment progress (status, timestamps, paid-amount tally) lives on the order. Shipping detail is not in this increment.

1. Context & Problem

The Orders capability starts as a skeleton with no working lifecycle: there is no way to build an order as a cart, mutate its lines, and move it through checkout to payment. Without that spine, nothing downstream - payment capture, kitchen tickets, POS sessions, loyalty - has an order to attach to.

This increment establishes the foundational sale-order lifecycle: a single record that is both the cart (in DRAFT) and committed order, with line-item mutation in draft, a checkout that validates and locks prices, and lifecycle logic that drives state transitions. It founds the Orders URD ORD feature that everything else in the module builds on.

2. Goals & Non-Goals

Goals

  • Build an order as a draft cart and add/update/remove line items while in DRAFT.
  • A controlled lifecycle from creation, through checkout into processing, to a paid/completed terminal state, with revert and cancellation paths.
  • Checkout that validates the cart (non-empty, prices ≥ 0, quantity ≥ 1) and locks prices.
  • Track payment progress on the order (status, timestamps, and a paid-amount tally) - a dedicated shipping-detail record is not built in this increment.
  • Provide working order request/response contracts and a checkout action.

Non-Goals

  • Refund / return flow - a URD non-goal (Planned).
  • Stock mutation - owned by Inventory.
  • Payment-provider integration - owned by Payment.
  • Combo fan-out, split/merge, customer link, and multi-currency - later refinements (URD-ORD-005, URD-ORD-012..016).
  • Check splitting, kitchen tickets, stations, POS sessions, reservations, loyalty points - later F&B features (CHK/KIT/STA/POS/RSV/PNT).

3. Success Metrics

MetricTarget / signal
Lifecycle coverage100% of sales flow through the order lifecycle (no order created outside DRAFT → checkout)
Checkout integrityZero orders reach PROCESSING with an empty cart, negative price, or zero quantity
Price stabilityPrices locked at checkout do not drift before payment
Cycle timeMedian time DRAFT → COMPLETED per merchant trends down

4. Personas & Use Cases

PersonaGoal in this feature
CashierBuild a cart, edit lines, check out, and take an order to payment
Manager / OwnerTrust that every sale follows a controlled, auditable order lifecycle

Core scenarios: create a draft cart → add/update/remove line items while in DRAFT → checkout (validate + lock prices) into PROCESSING → reach PARTIAL on partial payment or COMPLETED on full payment → cancel a non-terminal order or revert PROCESSING back to DRAFT.

5. User Stories

  • As a cashier, I want to create a draft order and add line items to it, so I can build up a sale as a cart.
  • As a cashier, I want to update or remove items while the order is in DRAFT, so I can correct the cart before committing.
  • As a cashier, I want to check out a draft order, so the cart is validated, prices are locked, and the order is ready for payment.
  • As a cashier, I want to revert a checked-out order back to DRAFT, so I can fix the cart after starting checkout.
  • As a cashier, I want the order to track its payment progress, so I can see what has been paid; no shipping-detail record is in this increment.
  • As a cashier, I want to cancel a non-terminal order, so mistaken orders don't proceed to payment.
  • As a manager, I want an order to reach PARTIAL on partial payment and COMPLETED on full payment, so the lifecycle reflects what was paid.

6. Functional Requirements

#RequirementURD ref
FR-1Create a draft order (cart) with line itemsURD-ORD-001
FR-2Add, update, and remove items only while in DRAFTURD-ORD-002
FR-3Adding the same product merges quantity onto the existing lineURD-ORD-003
FR-4Setting an item quantity to zero removes the lineURD-ORD-004
FR-5Checkout: DRAFT → PROCESSING (validate items, lock prices)URD-ORD-006
FR-6Checkout validation: non-empty cart, prices ≥ 0, quantity ≥ 1URD-ORD-007
FR-7Revert: PROCESSING → DRAFTURD-ORD-008
FR-8Cancel from DRAFT, PROCESSING, or PARTIAL (with optional reason)URD-ORD-009
FR-9Partial payment → PARTIAL statusURD-ORD-010
FR-10Full payment → COMPLETED statusURD-ORD-011
FR-11Track payment progress on the order (status, timestamps, paid-amount tally) - shipping detail is not built in this incrementURD-ORD-001

Full requirement text and acceptance criteria live in the Orders URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Data integrityItems are mutable only in DRAFT; checkout locks prices so totals can't drift before payment
Lifecycle integrityTransitions are constrained to the defined state graph; no skipping from DRAFT straight to a paid state
Tenancy & authzAll operations scoped per merchant and require authentication
ConsistencyCart mutation and checkout are transactional; partial failures don't leave a half-built order
i18nUser-facing labels/statuses are bilingual ({ en, vi })

8. UX & Flows

Key screens (in the merchant admin app): the cart/order builder, line-item editing, the checkout action, and a cancel/revert control. Full screen inventory lives in the Orders module flows.

9. Data & Domain

EntityRole
OrderThe order document - plays both cart (DRAFT) and committed-order roles; carries status, totals, and a paid-amount tally
Order line itemA line item - product reference, quantity, locked price
Payment progressTracked on the order via status, timestamps, and the paid-amount tally - no separate payment-detail entity in this increment
Shipping detailNot in this increment

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

10. Dependencies & Assumptions

Depends on

  • Product (Products) - order items reference product variants.
  • Payment (Payment) - payment events drive PARTIAL / COMPLETED / CANCELLED transitions.

Assumptions

  • A merchant context is present on every request (merchant-scoped, authenticated).
  • Products exist to add as line items before checkout.

11. Risks & Open Questions

Risk / questionMitigation / status
Cart mutation after checkout could corrupt locked totalsItems mutable only in DRAFT; revert to DRAFT is the supported path to edit
Lifecycle state values evolve as the package maturesURD states (DRAFT → PROCESSING → PARTIAL → COMPLETED / CANCELLED) are the source of truth; later features align to them
No refund/return path in this incrementOut of scope; owned by a Planned refund feature
Combo / split / multi-currency not yet supportedDeferred to later increments (URD-ORD-005, URD-ORD-012..016)

12. Release Plan & Launch Criteria

AspectPlan
PhaseP1 (foundation) - see URD feature catalog
RolloutAll merchants; no feature flag
MigrationNew entities; schema migrations for the order tables
Launch criteriaCreate cart → edit lines → checkout → payment verified end-to-end; checkout validation rejects empty cart / bad price / bad quantity; prices locked at checkout
MonitoringOrder volume per merchant, checkout rejection rate, lifecycle transition errors

13. FAQ

Can items be changed after checkout? No - items are editable only in DRAFT. Use the PROCESSING → DRAFT revert to make changes, or cancel.

What does checkout actually do? It validates the cart (non-empty, prices ≥ 0, quantity ≥ 1), locks prices, and moves the order DRAFT → PROCESSING so it is ready for payment.

What's the difference between PARTIAL and COMPLETED? PARTIAL means the order is underpaid; COMPLETED means it is fully paid.

Does this increment handle combos, bill splitting, or multi-currency? No - those are later features/refinements. This increment founds the core single-order lifecycle.

Where does stock get reserved or consumed? Not here - stock mutation is owned by Inventory. This feature only drives the order lifecycle.

References

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