The payoff for the promo-type config + multi-category groundwork. Verified on lumiere.staging6/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).
A · 2 Latte + 1 Tea → BOGO frees a Latte, 20% off the Tea → total $6.60. Each line's badge shows its own promotion.B · 2 Mocha (in BOTH categories) → BOGO frees 1 ($3.50); the % does not stack → total $3.50.C · entering a manual line discount clears the promotions (the Latte's BOGO badge is gone).D · the pay-dialog order-level discount is locked while promotions are applied — "remove the promotion to add a manual discount".