5473cc3 · migration · Gate 2 6/6 · regression 51/51The drink-shop promotion from the 06.06 New Features page. A per-shop, cashier-applied discount: for every 2 eligible drinks the cheaper one is free (sort by price, charge the higher). Built in two parts — a config module + the POS cart integration — shipped together. Verified on lumiere-coffee.
New settings page. Create a Buy 1 Get 1 Free program, scope it to a shop (or all shops)
and to eligible categories (or all products), then Activate it. Gate 2 created one and asserted it
persisted active in cafe.promotions.


When an active promo exists for the shop, an Apply «promo» button appears in the
cart. Clicking it pairs the eligible units and frees the cheaper of each pair, showing the program name as
the discount reason on the freed line + a Remove toggle. Gate 2 added 3× Americano
($3) → applied → 1 freed → net $6 (gross $9), label shown. The reason persists to
order_lines.discount_reason and prints on the receipt (default-named promo reads
“Buy 1 Get 1 Free”).


• Eligibility resolves on the cafe-native product model (Starter + cafe-master); Pro
odoo-master product ids degrade to ineligible.
• The whole drink (incl. its modifier extra) is freed — spec’s “modifiers not
discounted” is a future refinement.
• The pay-dialog order-level discount still stacks on top — no cross-discount guard yet.
• Editing the cart after applying doesn’t auto-recompute — cashier re-taps Apply.