Skip to content

PRD: Check split & merge

ModuleOrders (CORE-07)PRD IDPRD-CHK-001
StatusShippedOwnerSale squad
Date2026-04-02Versionv1.0
Packages@nx/sale · @nx/core · apps/clientURDCHK · ORD

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 of its checks are paid, and the POS and kitchen displays stay in sync through real-time events. The result: groups settle their own portions of a shared bill without forcing the floor staff into manual workarounds.

1. Context & Problem

A full-service venue regularly serves one table whose guests want to pay separately - different people pay for their own items, sometimes under a different customer profile - and equally needs to combine orders that were 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 SaleCheck aggregate on top of the existing sale-order lifecycle, the services that allocate items into checks and reconcile completion, and the order-merge path for combining two draft orders - closing a hard gap for F&B and any multi-guest bill scenario KICKO targets.

2. Goals & Non-Goals

Goals

  • A SaleCheck aggregate that splits 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 webhook.
  • Rollback of a split while no check has been paid yet.
  • Order merge for two PROCESSING orders in the same merchant/branch.
  • Real-time events (check.created/updated/merged/rolledBack/paid, order.merged) to keep POS and kitchen displays in sync.

Non-Goals

  • Refund / return flow - URD Non-Goal (Planned).
  • Payment provider integration itself - only the webhook 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

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 is split into multiple checks → items and quantities (and optionally a separate customer) are allocated to each → guests pay their own checks → the order completes once all checks complete; while nothing is paid, a split can be rolled back, and two draft orders for one 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 checks via the SaleCheck aggregateURD-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 webhookURD-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 merchant/branchURD-ORD-013
FR-8Emit real-time events: check.created/updated/merged/rolledBack/paid and order.mergedURD-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 scoped per merchant (x-merchant-id); merge restricted to the same merchant/branch; permission-gated
Real-timeCheck and merge state changes broadcast via WebSocket events for POS/KDS sync
Soft-deleteAll records use soft-delete
i18nUser-facing labels/statuses are bilingual ({ en, vi })

8. UX & Flows

Key screens (in apps/client): the order detail with a split-bill action, the per-check allocation editor, per-check payment, and an order-merge action.

9. Data & Domain

EntityRole
SaleCheckAn independently-payable group within an order - status, optional customer, totals
CheckItemA per-check allocation of an order item - item ref + quantity
SaleOrderThe parent order; completes when all its checks complete
Real-time eventsSaleCheckEvents - check.created/updated/merged/rolledBack/paid, order.merged

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 (@nx/payment) - the payment webhook 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 events arrive via the webhook 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; webhook-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 SaleCheck aggregate (schema + migration); 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/branch
MonitoringSplit volume per merchant, overallocation rejection rate, check-vs-order completion consistency, event delivery

13. FAQ

Can I split an order before checkout? No - a check is split from a checked-out (PROCESSING) order. 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 webhook, 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.