Skip to content

PRD: Check split & merge

ModuleSalePRD IDPRD-CHK-001
StatusReady to devFEATCHK · BANA-1433
EpicPlaneBANA-1563
Date2026-04-02Versionv1.0
Packages@nx/saleURDCHK
SurfaceSale · POS
OwnerPhát Nguyễn

TL;DR

Lets a full-service venue split one table's order into several independently-payable checks - each with its own items, quantities, and even a different customer - and merge separate orders back together. The order completes automatically once all checks are paid. Groups settle their own portions without forcing floor staff into workarounds.

Implementation status: the check aggregate, per-check allocation, split, merge, rollback, and payment-driven completion are built. Two design points differ from this spec in the current code: (1) the dedicated real-time check / order-merged event broadcasts are not yet wired (those emissions are stubbed out; only an order-level update fires when checks finish completing the order); and (2) the check-split guard currently admits only DRAFT orders, not the checked-out (PROCESSING) order this spec assumes.

1. Context & Problem

Full-service venues routinely handle tables where guests want to pay separately - different items, different people, sometimes different customer profiles - and need to combine orders opened separately for the same party. Treating an order as a single payable unit blocks both: there is no way to allocate specific items across payment groups, no way to complete an order check-by-check, and no clean way to fold one order into another.

This increment builds the check capability on top of the existing order lifecycle - allocating items into checks, reconciling completion, and the order-merge path for combining two draft orders - closing a hard gap for F&B and any multi-guest bill scenario BANA targets.

2. Goals & Non-Goals

Goals

  • Split a checked-out order into multiple independently-payable checks.
  • Per-check item allocation - assign specific items and quantities to each check, with overallocation guarded.
  • A different customer per check, so each payment group can carry its own customer link.
  • Order completes automatically once all of its checks complete, driven by the payment-success path.
  • Rollback of a split while no check has been paid yet.
  • Order merge for two PROCESSING orders in the same merchant.
  • Real-time check events (created / updated / merged / rolled-back / paid) and an order-merged event to keep POS and kitchen displays in sync.

Non-Goals

  • Refund / return flow - URD Non-Goal (Planned).
  • Payment provider integration itself - only the payment-success hand-off is wired (see Payment).
  • Tax engine / e-invoice issuance per check.
  • Stock mutation on check allocation (see Inventory).

3. Success Metrics

MetricTarget / signal
Split adoptionShare of full-service orders that use checks where a split-bill is requested
Allocation integrityZero checks allocated beyond the order's available item quantities
Completion accuracyOrder completes only when all of its checks are complete - no early/late closes
Rollback safetyRollback succeeds whenever no check is paid; never after a check has paid
Sync latencyPOS / kitchen reflect check & merge events in real time (target; dedicated check/merge broadcasts not yet wired)

4. Personas & Use Cases

PersonaGoal in this feature
CashierSplit a table's bill into checks, allocate items, take payment per check, merge orders
Server / HostCombine separately-opened orders for one party
Manager / OwnerTrust that an order closes exactly when every check is settled

Core scenarios: a checked-out order splits into checks → items and quantities (optionally a separate customer per check) are allocated → guests pay their own check → the order completes when all checks complete. Unpaid splits can be rolled back; two draft orders for the same party can be merged.

5. User Stories

  • As a cashier, I want to split a checked-out order into multiple checks, so each guest can pay their own portion.
  • As a cashier, I want to allocate specific items and quantities to a check, so the bill split reflects what each guest had.
  • As a cashier, I want to attach a different customer to a check, so each payment group keeps its own customer link.
  • As a cashier, I want the order to complete automatically once all checks are paid, so I don't have to close it manually.
  • As a cashier, I want to roll back a split while no check is paid, so I can fix a mistaken split.
  • As a server, I want to merge two orders for the same party, so a single bill represents the whole table.

6. Functional Requirements

#RequirementURD ref
FR-1Split a checked-out order into multiple independently-payable checksURD-CHK-001
FR-2Allocate specific items and quantities to each check, with overallocation guardedURD-CHK-002
FR-3Allow a different customer per checkURD-CHK-003
FR-4Order completes automatically when all of its checks complete, driven by the payment-success pathURD-CHK-004
FR-5Roll back a split while no check has been paidURD-CHK-005
FR-6Split one order into multiple, DRAFT onlyURD-ORD-012
FR-7Merge multiple orders, DRAFT only / same merchantURD-ORD-013
FR-8Emit real-time check events (created / updated / merged / rolled-back / paid) and an order-merged event - not yet implemented: the check/merge event emissions are stubbed out; today only an order-level update fires when checks complete the orderURD-CHK-001..005

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 integrityPer-check allocation never exceeds the order's available quantities; completion state is derived, not hand-set
ConsistencySplit, allocation, rollback, and merge are transactional - a partial failure leaves no half-written checks
Tenancy & authzAll operations confined to the user's own merchant; merge restricted to the same merchant; permission-gated
Real-timeCheck and merge state changes are intended to broadcast via real-time updates for POS/KDS sync - the dedicated check/merge broadcasts are not yet wired (only an order-level update emits on check-driven completion)
RecoverabilityRecords are recoverable, never hard-removed
i18nUser-facing labels/statuses are bilingual ({ en, vi })

8. UX & Flows

Key screens: order detail with a split-bill action, per-check allocation editor, per-check payment, and order merge.

9. Data & Domain

EntityRole
CheckAn independently-payable group within an order - status, optional customer, totals
Per-check item allocationA per-check allocation of an order item - item ref + quantity
OrderThe parent order; completes when all its checks complete
Real-time eventsIntended real-time check events (created / updated / merged / rolled-back / paid) and an order-merged event - not yet emitted in the current code

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

10. Dependencies & Assumptions

Depends on

  • Sale Order lifecycle (URD-ORD) - a check is split from a checked-out order; merge operates on draft/processing orders.
  • Payment - the payment-success path drives check completion and, in turn, order completion.
  • Customer - optional per-check customer link.

Assumptions

  • The order has been checked out (PROCESSING) before it is split into checks.
  • Payment confirmation arrives via the payment-success path to mark a check paid.

11. Risks & Open Questions

Risk / questionMitigation / status
Allocation could exceed available quantitiesOverallocation is guarded at allocation time
Rollback after a check has paidRollback only permitted while no check is paid
Completion races as multiple checks pay near-simultaneouslyCompletion derived from all-checks-complete; payment-success-driven, idempotent
Stock not mutated per check allocationOut of scope - owned by Inventory
Tax / e-invoice per checkOut of scope - owned by Tax & Invoice

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - see Orders feature catalog (MoSCoW: Should)
RolloutAll merchants; no feature flag
MigrationNew check aggregate; no data backfill
Launch criteriaSplit → allocate → pay-per-check → order auto-completes verified end-to-end; rollback verified while unpaid; order merge verified for same merchant
MonitoringSplit volume per merchant, overallocation rejection rate, check-vs-order completion consistency, event delivery

13. FAQ

When can an order be split into checks? By design a check is split from a checked-out (PROCESSING) order, but in the current code the split guard admits only DRAFT orders (the status check needs aligning with the intent). Order split/merge itself is DRAFT-only.

Can each check have its own customer? Yes - a different customer can be attached per check.

When does the order complete? Automatically, once all of its checks complete - driven by the payment-success path, not a manual close.

Can I undo a split? Yes, by rolling it back - but only while no check has been paid.

Does splitting a bill change stock or issue invoices per check? No - stock mutation is owned by Inventory and tax / e-invoice issuance is out of scope here.

References

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