Skip to content

PRD: Order split, merge & rollback

ModuleSale (CORE-07)PRD IDPRD-ORD-002
StatusShippedOwnerSale squad
Date2026-06-15Versionv1.0
Packages@nx/sale · @nx/coreURDORD

TL;DR

Lets a cashier restructure a table's draft orders without rebuilding them by hand. Split carves one draft into several new drafts, allocating chosen items and quantities (and an optional name / customer) to each; a line whose full quantity is taken moves whole, a partial take splits the line and leaves the remainder behind. Merge folds several drafts into one and cancels the sources. Rollback undoes a merge - every item walks back to the exact order it came from. Combos always travel as one unit, an append-only transfer-history lineage stamps every move, the reserved stock travelling with the items, and each operation is one all-or-nothing transaction that re-prices the orders it touches.

1. Context & Problem

A draft order (ORD) already plays both cart and committed-order roles, but a real service counter constantly reshapes drafts: two friends who sat together now want separate bills; three tabs opened by mistake should be one; a merge done in haste must be undone. The order lifecycle alone offers no way to move items between drafts - a cashier would have to cancel and re-key, losing prices, customer links, and the stock already reserved.

Without a first-class restructure path, the counter either refuses these everyday requests or rebuilds orders manually, which drops reserved inventory, mis-prices combos, and leaves no trace of what moved where. This increment closes that gap on top of the existing draft order: declarative split / merge / rollback operations that move items (and their reserved stock) atomically, keep combos intact, and record a reversible lineage so a merge can always be walked back.

2. Goals & Non-Goals

Goals

  • Split a draft into N new drafts by allocating items and quantities, with full-quantity moves and partial-quantity line splits.
  • Merge one or more drafts into a target draft and cancel the emptied sources.
  • Rollback a merge so every item returns to its original source order, restored to draft.
  • Keep combos atomic - a lead and all its children always move together with full quantity.
  • Stamp an append-only transfer-history lineage on every moved item so the path is auditable and reversible.
  • Carry reserved stock (allocation usages) with the items and re-price every affected order, all inside one transaction.

Non-Goals

  • Bill splitting into independently-payable checks - that is CHK (PRD-CHK-001); this PRD reshapes orders, not payment checks.
  • Splitting or merging non-draft orders - restructure is DRAFT-only (C-02).
  • Restructuring an order that has active checks - blocked until the checks are rolled back.
  • Payment, refund, or stock-mutation logic - owned by Payment and Inventory.

3. Success Metrics

MetricTarget / signal
Reshape without re-keyA cashier splits, merges, or rolls back from one action - no cancel-and-re-enter
AtomicityA failed sub-step leaves every participating order exactly as it was
Combo integrityNo split ever orphans a combo lead or child; combo members never partially move
ReversibilityAny merge can be rolled back to the original orders using only the recorded lineage
Stock continuityReserved stock follows the items - no double-reservation, no lost reservation

4. Personas & Use Cases

PersonaGoal in this feature
CashierSplit a shared tab into separate bills, or combine mistaken tabs, in one step
ManagerUndo a wrong merge cleanly, with the original orders restored intact
OwnerTrust that every reshape keeps prices, combos, and reserved stock correct

Core scenario: a table of four opened one draft but wants two bills. The cashier splits the draft - assigning two mains and a shared starter (full quantity) to bill A and the rest to bill B - and the system creates two new drafts, moves the lines (splitting the shared-by-quantity ones), carries the reserved stock, re-prices both, and cancels the now-empty original. Later they realise it should have been one bill, merge B back into A, and - spotting a third error - roll the merge back so B is restored exactly as it was.

5. User Stories

  • As a cashier, I split a draft into new drafts by choosing items and quantities per bill, so a shared tab becomes separate bills in one step.
  • As a cashier, I take a line's full quantity or just part of it, so I can move a whole dish or just two of five.
  • As a cashier, I merge several drafts into one, so mistaken separate tabs become a single bill and the others close.
  • As a manager, I roll a merge back, so a wrong combine is undone and every item returns to its original order.
  • As an owner, I want combos to move as a unit and reserved stock to travel with the items, so reshaping never corrupts a bundle or its stock.

6. Functional Requirements

#RequirementURD ref
FR-1Split a DRAFT order into N new DRAFT orders, each allocated specific items and quantities, with an optional name and customer per new orderURD-ORD-012 · URD-ORD-017
FR-2A line whose full quantity is assigned moves whole; a partial assignment splits the line, leaving the remaining quantity on the sourceURD-ORD-017
FR-3A combo's lead and all its children must move together, at full quantity, into the same target; partial or orphaning combo moves are refusedURD-ORD-018
FR-4A fully-emptied source is cancelled (FULL_SPLIT); a partially-split source is retained and re-pricedURD-ORD-017
FR-5Split refuses a non-positive quantity, an unknown item, or an over-allocation (assignments exceeding the line's available quantity)URD-ORD-017
FR-6Merge moves every item of one or more DRAFT source orders into one DRAFT target and cancels the sources (MERGED_INTO_<target>)URD-ORD-013 · URD-ORD-019
FR-7Only orders of the same merchant and sale channel may mergeURD-ORD-019
FR-8Every transferred item appends a transfer-history entry (source, target, time, quantity) forming the split / merge lineageURD-ORD-020
FR-9A merge can be rolled back while the target is still DRAFT and was merged; items return to their original sources via the lineage, and each source is restored to DRAFTURD-ORD-021
FR-10On rollback, quantity added to a line after the merge stays on the target; only the originally-transferred quantity returns to the sourceURD-ORD-021
FR-11Split, merge and rollback are atomic; allocation usages clone (split) / move (merge) / cancel (rollback) in lock-step and affected orders are re-pricedURD-ORD-022
FR-12An order with active checks cannot be split, merged, or rolled backURD-ORD-023

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

7. Non-Functional Requirements

AreaRequirement
AtomicityEach operation is one all-or-nothing transaction - any failed sub-step (item move, source cancel, reprice) rolls the whole reshape back
ConcurrencyParticipating orders and their items are row-locked for the duration so two cashiers cannot reshape the same draft at once
AuditabilityThe transfer-history lineage is append-only - a move adds an entry, a rollback pops only the last one; corrections are new state, never edited history
Stock continuityReserved allocation usages travel with the items - cloned on split, moved on merge, cancelled on rollback - so reservations are never lost or duplicated
PricingEvery order whose items changed is re-priced; split re-prices both the new orders and a retained partial source
Tenancy & authzAll operations scoped per merchant (x-merchant-id) and gated by split / merge / rollback permissions
i18nUser-facing labels and rejection reasons are bilingual ({ en, vi })

8. UX & Flows

Split a draft

Merge then roll back

The cashier surface offers a split panel (assign items and quantities to one or more new bills, name each, optionally set a customer), a merge action (pick sources and a target), and a rollback action on a merged draft.

9. Data & Domain

EntityRole in reshape
SaleOrderThe draft being reshaped; carries the markers stamped by each operation (split / merge timestamps, cancellation reason, origin link)
SaleOrderItemThe line that moves between orders; a full move relocates it, a partial move splits it into a new line
TransferHistoryEntryOne append-only step of a line's lineage - source order, target order, time, and quantity moved; the last entry tells rollback where to return the line

Conceptual only - full schema and invariants live in the sale domain model. Order-to-order relations are soft references; integrity is enforced in the split / merge services, not by database constraints.

10. Dependencies & Assumptions

Depends on

  • Sale order lifecycle (ORD) - restructure operates only on DRAFT orders and reuses checkout / cancel transitions.
  • Combo fan-out (URD-ORD-005) - the lead / child structure that the atomicity guard protects.
  • Bill splitting (CHK) - an order with active checks is blocked from reshape until the checks are rolled back.
  • Reserved stock (allocation usages) - cloned / moved / cancelled in step with the items.
  • @nx/core - sale order and item models, status helpers, and the transfer-history shape.

Assumptions

  • A cashier reshapes only their own merchant's and channel's drafts.
  • Reserved stock for the source order exists before a split or merge and is the authority for what travels.

11. Risks & Open Questions

Risk / questionMitigation / status
A partial failure leaves orders half-reshapedEach operation is one transaction with row locks - any failure rolls the whole reshape back
A split orphans a combo lead or childUp-front atomicity guard - combo members must all move together at full quantity, else refused
Rollback can't tell where an item came fromThe append-only transfer-history lineage records each move; rollback reads the last entry per line
Quantity changed after a merge, then rolled backSurplus added after the merge stays on the target; only the originally-transferred quantity returns
Two cashiers reshape the same draft at onceParticipating orders and items are row-locked for the operation's duration
Reserved stock lost or double-countedAllocation usages clone on split, move on merge, cancel on rollback - in lock-step with the items

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - ORD reshape capability in the URD feature catalog
RolloutAll merchants; no feature flag
MigrationNone - reshape runs on the existing draft order and item models; transfer-history is an additive field
Launch criteriaSplit allocates items / quantities into new drafts (full and partial moves) and cancels or re-prices the source; merge folds sources into a target and cancels them; rollback restores sources from the lineage; combos move atomically; active-check orders are blocked; every operation is atomic with reserved stock following the items
MonitoringReshape error rate by reason (over-allocation, orphaned combo, active checks, not-merged), rollback success rate, stock-reservation consistency after reshape

13. FAQ

How is this different from check splitting? Check splitting (CHK) divides one order into independently-payable checks for the same bill. This reshapes the orders themselves - moving items between separate drafts. See PRD-CHK-001.

Can I split or merge an order that's past draft? No - reshape is DRAFT-only (C-02). Once an order checks out, its items are locked.

What happens to a combo when I split? It moves as one unit. The lead and all its children must go to the same new bill at full quantity; assigning only part of a combo is refused before anything saves.

If I take only some of a line's quantity, what happens to the rest? The line splits - the taken quantity becomes a line on the new order, the remainder stays on the source, and both are re-priced.

Can every merge be undone? Yes, while the target is still draft and has no active checks. Rollback uses each line's transfer-history lineage to return it to the exact order it came from; quantity added after the merge stays on the target.

Does reserved stock survive a reshape? Yes - reserved allocation usages are cloned on split, moved on merge, and cancelled on rollback, in lock-step with the items, so reservations are never lost or duplicated.

References

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