← All tests

% Discount — apply-side at the POS STAGING

The payoff for the promo-type config + multi-category groundwork. Verified on lumiere.staging 6/6 + unit suites % 7/7 · combine 7/7 + smoke 5/5.

What. A "% Discount" promotion now applies at the till alongside BOGO. The percentage comes off each ELIGIBLE line's gross (a line-item discount, not the order total — Narong 2026-07-21). Multiple promotions can be active at once; one "Apply Promotion" button applies them all, each to its own drinks. Money math is pure + unit tested (lib/promotions/percent.ts + combine.ts). No migration (the config side shipped earlier).

Decisions (defaults — flag if Narong wants otherwise)

Multiple active promos → one button auto-applies all, each to its eligible lines (no cashier picker).
A line in two promo categories → each line gets at most ONE promotion. BOGO (a cart-global pairing) claims its lines first; % fills the rest; among several %, the largest wins. No promo-on-promo stacking.
Manual vs promo → mutually exclusive. Entering a manual discount clears promotions; the pay-dialog order-level discount is locked while promotions are applied.

Live POS proof (lumiere.staging)

Setup: a BOGO scoped to BOGO Drinks + a 20% promo scoped to Percent Drinks, both active. Latte $5 ∈ BOGO Drinks; Tea $2 ∈ Percent Drinks; Mocha $3.50 ∈ both (overlap).

both promos applied
A · 2 Latte + 1 Tea → BOGO frees a Latte, 20% off the Tea → total $6.60. Each line's badge shows its own promotion.
overlap BOGO wins
B · 2 Mocha (in BOTH categories) → BOGO frees 1 ($3.50); the % does not stack → total $3.50.
manual clears promos
C · entering a manual line discount clears the promotions (the Latte's BOGO badge is gone).
pay dialog discount locked
D · the pay-dialog order-level discount is locked while promotions are applied — "remove the promotion to add a manual discount".

Checks

% engine — line-item, rounding, clamp, cap (unit)7/7
Combination — %-only, BOGO-only, both, overlap→BOGO, best-%, appliesToAll, none (unit)7/7
POS A: BOGO + % apply together → $6.60, distinct per-line reasonspass
POS B: overlap → BOGO wins, % does not stack → $3.50pass
POS C: manual discount clears promotionspass
POS D: pay-dialog order-level discount locked under promotionspass
Staging smoke5/5

nix-cafe staging 284837e (Worker version 59859107). No migration. Starter tenant — eligibility resolves cafe-native product UUIDs. Parked for the scheduled prod promote.