# retail.promotion-apply Prices a basket under the retailer's offers: buy one get one free, 3 for 2, percent off, an amount off, and multibuys like 3 for £10, including mix-and-match across SKUs. When offers compete for the same items the customer gets the combination that saves the most, and every discount is shared back to the lines to the penny. ## The four kinds Every promotion works on groups of `groupSize` qualifying items. Items from any SKU in `skus` mix and match. | kind | each group | examples | |---|---|---| | `free-items` | the cheapest `freeItems` of the group are free | BOGOF (2, 1), 3 for 2 (3, 1) | | `group-price` | the group costs `amount` (never more than it did) | 3 for £10 (3, £10.00), meal deal | | `percent-off` | `basisPoints` off the group, rounded half up | 25% off (1, 2500) | | `amount-off` | `amount` off the group, at most the group's price | £1 off (1, £1.00), £5 off any 2 | ## How items are grouped The qualifying items are lined up from most to least expensive (ties in basket order) and cut into consecutive groups; leftovers that do not make a full group pay full price. This is the usual retailer rule ("cheapest item free") and it is also the best one for the customer: 3 for 2 on items at £10, £8, £6 and £3 makes one group of £10, £8, £6 and gives the £6 item free, not the £3 one. A percent-off with `groupSize` 1 rounds each item, so three items at £19.99 with 25% off save 3 x £5.00 = £15.00, not 25% of £59.97 = £14.99. That is what a till that discounts item by item does, and it is the only way returns of single items stay consistent. ## When promotions compete Each item takes part in at most one deal. Promotions that share a SKU in the basket compete; for each set of competing promotions, every order of applying them is tried (each takes its groups from the items still free), and the order with the largest total saving wins. Ties go to the first order in the promotions' given order, so the result is deterministic. Trying orders beats taking the biggest single saving first: with A at £10, two of B at £10, "50% off B" and "BOGOF on A or B" each save £10 alone, so a biggest-first rule may take 50% off both Bs (£10) and leaves A alone, where BOGOF on A and one B plus 50% off the other B saves £15. Up to six promotions may compete for the same items (720 orders); more is an error rather than a slow checkout. Promotions with no qualifying items in the basket do not count. ## Allocation back to lines Each group's discount is split across the items in the group in proportion to their prices with `money.allocate`, so the lines' discounts add up to the deal's discount exactly and a returned item carries its fair share (`retail.refund-calculate` depends on this). The free item in a 3 for 2 is not the only line discounted; all three share the saving. ## Not modelled Stacking (an item in two deals at once), basket-level thresholds ("£5 off when you spend £40", which is a coupon: `retail.coupon-validate`) and loyalty prices. Promotion ids must be unique.