"You're right, we can just use Free Items, probably more straightforward."
If an item ends up costing nothing, the Discounts page now calls it a Free Item. It used to call it a discount of the full price, which is technically true and reads as nonsense — a Special Gift given away for free was filed under "money off", and the Free Items count stayed at zero.
The page doesn't guess how many units were given away — it reads a number the till records at the moment of sale. That is deliberate, and it's the part worth protecting: a buy-one-get-one that frees one coffee out of three takes a third off the line, which is arithmetically identical to a 33% discount. Nothing about the money can tell those two apart, so if the page worked it out from the total it would get BOGO wrong.
But that ambiguity only exists for partial discounts. When an item is discounted to zero, every unit on the line is free — there is nothing left to work out. Two things reach zero without the till recording free units: a cashier taking 100% off a line, and a whole-order discount that covers the entire order. Those are the cases that were being mislabelled, and those are the only cases that changed.
| Case | Before | Now |
|---|---|---|
| BOGO frees 1 of 3 coffees | Free Item ×1 | Free Item ×1 — unchanged |
| Cashier takes 100% off a line | Discount | Free Item, whole quantity |
| Whole order given away (Special Gift) | Discount on every item | Free Item on every item |
| Any partial discount | Discount | Discount — unchanged |
The recorded number always wins where it exists, so nothing a promotion actually counted has been overwritten. Only rows that reach zero with nothing recorded fall back to "all of it was free".
Filtering by Free Items now returns the fully discounted rows too, and filtering by Discount stops returning them. Both directions are checked, because a filter that quietly leaks a row is as wrong as one that loses it.
One case worth knowing. On the rare order that carries both a line discount and a whole-order discount, and the two together bring an item to zero, the line's own discount stays listed as a discount (it wasn't the whole price on its own) and the order-level row becomes the free one. The item is counted free once, on the row that finished the job.
| Suite | Result |
|---|---|
| Local, incl. a control run against the old rule | 8/8 |
| Staging, against the deployed build | 6/6 |
| Existing Discounts ledger suite (regression) | 15/15 |
| Staging smoke regression | 5/5 |
The important check here is the one that guards against over-counting: the BOGO line freeing one coffee out of three must still count as one, not three. A change that fixed the 100% case by working everything out from the money would break it, and the Free Items total is what catches that — 6 is correct, 8 would mean it slipped. Run against the previous version, the same checks drop to 3 of 8.
No database change was needed. Verified on staging.