Allocating Order-Level Discounts Across Line Items For Honest POAS

A 15% sitewide code on a mixed cart has to be pushed back to each line before POAS is trustworthy. Here are the three allocation methods — proportional-to-price, margin-weighted, and targeted-SKU — and when each is the right call.
Quick answer
Allocate a proportional-to-price share of the discount to every line by default. Switch to margin-weighted when your COGS varies sharply across SKUs, and to targeted-SKU allocation when the code is restricted to a specific product, collection or BOGO pair. Without allocation, POAS is inflated on high-margin lines and understated on the ones that actually took the hit.
Order-Level Discount Allocation for POAS
Splitting a cart-level discount back to each line item so per-SKU profit and POAS reflect what each line actually contributed.
Order-level discount allocation is the accounting step that takes a single cart discount — a 15% sitewide code, a €20 threshold reward, a BOGO, free shipping — and distributes it across the individual line items in the order. Until that split happens, every line still shows its full pre-discount price, which means gross profit and POAS look correct at the order level but are wrong at the SKU, campaign or ad-set level. The three methods that matter are proportional-to-price allocation, margin-weighted allocation, and targeted-SKU allocation. Shopify exposes its own split in the discount_allocations array; other platforms make you compute it yourself.
POAS — profit on ad spend — only works if the profit number is honest per order. And per-order profit is only honest if every line item carries its fair share of whatever discount the customer actually used at checkout.
Why unallocated discounts break POAS
Imagine an apparel cart: a €90 jacket at 55% margin and a €30 tee at 20% margin, with a SAVE15 sitewide code taking €18 off the order. If you dump the full €18 against the jacket, its contribution profit collapses from €49.50 to €31.50 and the tee looks pristine at €6.
Feed that into a paid-media report and the Meta campaign that drove jacket sales gets punished while the campaign pushing the tee looks like a hero. Budget follows the number. Bad allocation means bad budget decisions, week after week.
The default is almost always wrong
Most reporting stacks — including a lot of paid-media dashboards — either ignore order-level discounts entirely (POAS uses gross revenue, not net) or dump 100% of the discount against the most expensive line. Both are silently wrong. Check what your stack does before you trust the number.
Method 1: Proportional-to-price allocation
The default method: each line gets a share of the discount equal to its share of the pre-discount subtotal. For the €90 jacket + €30 tee cart with an €18 code, the jacket absorbs €13.50 (75% of the discount) and the tee absorbs €4.50 (25%). This is what Shopify's discount_allocations array gives you for sitewide codes, and it's what ROAS measurement tooling typically assumes.
Use it when your SKUs have broadly similar margin structure — an apparel store where most items sit in a 40-60% margin band, a beauty store where the SKUs in a bundle are all house-brand. It's simple, defensible, and matches what your finance team sees on the invoice. See proportional-to-price discount allocation explained for the full worked math.
Method 2: Margin-weighted allocation
Instead of splitting by revenue share, split by contribution-margin share. The high-margin line can absorb more of the discount without going underwater; the thin-margin line can't. If your jacket is 55% margin and your tee is 20% margin, margin-weighted allocation puts more of the €18 onto the jacket than proportional does.
This is the right call when COGS varies sharply across the cart — a mixed-COGS beauty order with a house-brand serum next to a third-party device, or an electronics store bundling a low-margin console with high-margin accessories. Margin-weighted discount allocation for mixed-COGS carts spells out the formula and edge cases; the sibling comparison, proportional-to-price vs margin-weighted, walks through which POAS number is the one you should actually report.
Rule of thumb
If the highest-margin SKU in a typical cart is more than 2× the margin of the lowest-margin SKU, proportional-to-price will systematically flatter one channel and punish another. That's the trigger to move to margin-weighted.
Method 3: Targeted-SKU allocation
Some discounts are restricted by design — 20% off the sale collection, buy one get one free on hoodies, free shipping when you spend €75. These aren't cart-wide, so allocating them cart-wide is factually wrong. The discount belongs on the lines it was authorised against.
The three common cases each have their own guide: targeted-SKU discount allocation for category and collection codes, allocating BOGO discounts to the free line rather than the paid one, and treating free shipping as a line-item discount in POAS math. Free-gift-with-purchase is the odd one out — no discount line exists, so you have to allocate COGS directly against the promoted SKU. Stacked-code attribution covers what to do when two of these hit the same cart.
How to detect that your allocation is off
Three quick diagnostics. First, pull orders with a discount code applied and check whether every line in the order has a non-zero allocated_discount field — if some lines are blank, your ETL is dropping the split. Second, sum the per-line allocated discounts and compare to the order-level discount total; they should match to the cent.
Third, segment POAS by discount-code-used vs no-code-used. If discounted orders show POAS more than 30% below undiscounted orders on the same campaign, your allocation is probably fine. If they look identical, you're almost certainly ignoring discounts entirely. Reading Shopify's discount_allocations array for honest POAS covers the specific field mapping.
One reporting layer, three switches
You don't have to pick one method forever. A mature POAS pipeline runs proportional-to-price by default, switches to margin-weighted when a cart crosses a margin-variance threshold, and switches to targeted-SKU whenever the discount metadata says the code was restricted. That's three rules, applied at ingest, not three separate reports.
Experiment ideas once your allocation is honest
Once per-line POAS is trustworthy, the tests you can run change. You can A/B test whether a 10% sitewide code or a 20% collection-restricted code drives better POAS on the same paid-social audience. You can test whether swapping BOGO for a threshold-based free-shipping offer shifts AOV without collapsing margin on the promoted SKU.
You can also finally answer the campaign-level question: does the Meta prospecting ad-set that leans on codes actually produce profitable customers, or does it just look profitable because the code is invisible in your reporting? That's the question honest allocation exists to answer.
Frequently asked questions
Proportional-to-price splits the discount by each line's share of the pre-discount subtotal. Margin-weighted splits it by each line's share of the pre-discount contribution margin. When margins are similar across SKUs the two produce nearly identical numbers; when margins vary sharply, margin-weighted moves more of the discount onto high-margin lines.
Yes — Shopify exposes a discount_allocations array on each line item that already splits sitewide codes proportional-to-price. The catch is that many reporting tools ignore the array and use the order-level discount_total instead, which is why POAS numbers often look off. Read the array directly at ingest.
Put the full discount on the free line, not the paid one. The paid line is priced at its normal margin; the free line has a 100% discount and negative contribution equal to its COGS. Splitting the discount across both lines makes the paid line look artificially unprofitable and hides where the promotion cost actually lives.
Treat shipping cost as a line-item discount allocated proportional-to-price across the cart, or as a flat order-level cost line, depending on how your finance team books it. Either way it must reduce net contribution — ignoring free shipping is one of the biggest silent POAS distortions on stores that use a €50-€75 shipping threshold.
There's usually no discount line for a GWP — the gift is added as a €0 line item. You have to allocate its COGS as a cost against the promoted parent SKU, not the gift line itself. Otherwise the promoted SKU's POAS looks pristine while the gift's COGS floats around unattributed.
Proportional-to-price is the right default for most stores because it matches what Shopify already computes and what your finance team sees on the invoice. Move to margin-weighted only when your typical cart contains SKUs with materially different margin profiles — house-brand plus third-party, high-margin plus low-margin bundles.
Allocate each code independently against the lines it applies to, in the order the platform applies them. A 10% sitewide code splits proportional-to-price across all lines; a category code on top of it splits only across eligible lines using the already-discounted prices as the base. Stacked-code attribution covers the edge cases.
ROAS is revenue-based, so it's typically computed on gross revenue and doesn't care about allocation. POAS is profit-based, so allocation directly moves the number. That's why teams that switch from ROAS to POAS suddenly discover their allocation logic exists — it was there all along, just invisible.
You can, but then you're running ROAS, not POAS. The whole point of POAS is that it accounts for the real profit each order produced, which means it must account for the codes customers used. Ignoring discounts turns POAS back into a slightly-worse ROAS and defeats the reason for computing it.
Any time you launch a new promotion mechanic — a first BOGO, a threshold free-shipping offer, a stacked loyalty discount — audit the next week of orders line by line. Confirm the per-line allocated discounts sum to the order-level discount total, and that the split matches the method you intended.
Test ideas before you ship them
Run unlimited A/B tests, attach hypotheses to outcomes, and build a searchable archive of what works — and what doesn't.