If marketing optimises ROAS while finance optimises a different export, you will scale into unprofitability with a smile.
One formula
The important part is not a pretty chart. It is that HTTP and background refresh call the same compute so numbers cannot fork.
ROAS still lies sometimes
Even with P&L, channel ROAS can disagree with warehouse-attributed revenue. When we show that gap, it is a feature. Hiding it is how trust dies. Related: ROAS looks fine. A full worked month showing exactly how a 2.4x blended ROAS can still leave you £455 short is laid out in true profit from Shopify ads.
What you must fill in
COGS, shipping cost assumptions, and payment fees are not optional if you want profit language. Empty COGS coverage should make margin look incomplete, not heroic.
Questions people actually ask
Do you invent COGS when missing?
No. Incomplete COGS should show as coverage problems, not fake averages pretending to be truth.
Why cache P&L at all?
Scale. Many concurrent operators. Closed windows can cache longer; live windows stay short. No per-instance memory cache that survives a COGS edit on another worker.
Is Meta ROAS the same as P&L?
No. Margin-adjusted ROAS panels can show attribution gaps between Meta-reported and warehouse-attributed revenue when coverage allows.
Is P&L cache always on?
It is flag-gated. Off means live compute every time (rollback path).
How to build a Shopify P&L that matches the bank
This takes about two hours the first time and roughly twenty minutes a month afterwards. Everything here comes out of Shopify and your own invoices, with no extra software.
- Start from the waterfall, not from ROAS. The order is fixed and worth memorising: gross sales, minus discounts, minus returns, equals net sales. Minus COGS equals gross profit. Minus advertising, shipping cost, payment fees and app fees equals contribution. Everything below that line is overhead and salaries. If a conversation about profit does not start at the top of this list, it is a conversation about ROAS wearing a disguise.
- Take the top of the waterfall from Shopify directly. Open Analytics → Reports → Sales and pick the same date range you will use everywhere else. Shopify already breaks out gross sales, discounts, returns, net sales and shipping charged. Do not rebuild these from an order export: you will get a slightly different number and spend a week arguing about which is right.
- Fill in cost per item, because nothing works without it. COGS lives on the variant, under Cost per item. Until it is populated, every profit figure Shopify shows you is incomplete. Bulk edit or CSV import it, and include landed cost where you can: unit price, freight in, and duty. Leaving duty out is the most common way a healthy looking margin turns out to be imaginary.
- Take payment fees from the payouts, not an assumption. Do not apply a flat percentage. Go to Finances → Payouts and take the actual fees for the period, and add fees from any other gateway such as PayPal. Card mix, international cards and currency conversion move this more than people expect.
- Use what shipping actually cost you, not what you charged. Shipping charged is revenue and already sits in the Shopify report. Shipping cost comes from your carrier invoices and is usually the single biggest gap between a store's spreadsheet and its bank balance. Add packaging and pick-and-pack if you pay a third party.
- Add the ad spend from the platforms, not the attributed spend. Take total spend from each platform for the same date range, including the campaigns nobody talks about. This is a cash number and it is not up for attribution debate. Attribution decides which revenue to credit, never how much you actually paid.
- Calculate contribution margin, then MER alongside it. Contribution divided by net sales gives you the percentage that survives to cover overhead. Then divide total revenue by total ad spend for blended MER. MER and platform ROAS will disagree, and the gap is information rather than an error: it is the size of the claim your platforms are making on the same sales.
- Fix the date range and the cadence, then never move them. Pick calendar months or fixed 4-week periods and stay there. Most profit arguments are actually two people comparing different windows, or one person comparing a window that includes a promo with one that does not.
When contribution is healthy but a channel still feels wrong, the mismatch is usually attribution rather than maths. That case is unpicked in when ROAS looks fine and the store still hurts.
If the numbers are the problem: Ralph puts real orders, costs and refunds next to the ads, and leaves a blank where a cost price is missing rather than inventing a margin. See Ralph · True profit · Docs