From Narong's "Polishing Work" list (Notion, 2026-07-17). Verified on lumiere.staging
7/7 + staging smoke 5/5.
cafe.promotions.discount_percent NUMERIC(5,2), kept nullable so
"no percentage" (a BOGO) stays distinguishable from "0% off". promo_type already existed, so every
pre-existing row is already a valid 'bogo' — no backfill needed.




promo_type='percent', discount_percent=15 (asserted in the DB).
bogo string.
| Modal shows a type selector, defaulting to BOGO (no percent field) | pass |
| Choosing % Discount reveals the percent field | pass |
| Percent validation rejects 0 and >100 | pass |
Create 15% → DB has promo_type='percent' + discount_percent=15 | pass |
| List badge reads "15% off", no raw type string | pass |
| BOGO still applies unchanged with a % promo present ($7) | pass |
Migration via migrate.js (the prod path) — idempotent both ways | pass |
| Staging smoke | 5/5 |
nix-cafe staging d1dd6d7 ·
nix-OS-backend staging 9ea31f1 (migration 20260720120000_cafe_promotions_discount_percent,
already applied to nix_db_staging). Parked for the scheduled prod promote — it rides the same
node migrate.js run the n1 POS-user-role migration already needs.