Cost per variant, entered once, applied to every order that contains it. Enter them in the cost modal or bulk-load them by CSV.
Cost of goods sold is the first deduction on the profit ladder, and everything below it inherits whatever you put here. Gross profit is revenue minus COGS. Contribution margin starts from gross profit. Net profit starts from contribution margin. A cost that is wrong by two dollars is wrong by two dollars on every order containing that variant, in every report, for as long as it stands.
This is also the only cost NetNet cannot obtain for you. Shopify knows what you charged; it does not know what you paid your supplier. Ad platforms report their own spend, carriers report their own labels, gateways report their own fees — all of those sync. COGS is the number that has to come from you, which is why it is the first thing set up and the one place worth being slow and careful.
You are not costing orders. You are describing what each variant costs, once, and the calculator applies that description to every order that contains it — past, present and future. Set a cost today and it governs the orders that arrive tomorrow without you touching them again.
The practical consequence is that setup effort scales with catalogue size, not with order volume. A store doing four hundred orders a month across forty variants configures forty things, not four hundred.
Best for a handful of variants, or for anything needing a date range. Clicking a cost opens one modal that sets the default cost and any cost periods together — there is no inline editing to half-save.
Best for a first load, or any time you are setting more than a dozen costs at once.
A variant with no cost is not skipped. It is estimated from your default margin — average unit price multiplied by one minus that margin — and displayed with a ≈ so you can see at a glance which figures are real. The estimate uses exactly the same formula the profit calculator uses, so what you read in the table cannot drift from what lands in your P&L.
That design is deliberate. The alternative is a dashboard with holes in it, where gross profit is overstated by however much unconfigured stock cost you, and nothing on screen says so. An estimate is honest about being an estimate; a blank is not.
The coverage strip above the table keeps the split visible — how many variants are exact, how many are estimated at your current default margin, and a link to change that margin. When the estimated count stops moving, you are done.
Supplier prices move, and a single cost per variant forces a choice between two wrong answers: apply the new price backwards and rewrite history, or leave the old price standing and understate today.
Cost periods remove the choice. Each cost can carry an effective-from and effective-to date, so a price rise in March applies from March while February keeps what it actually paid. Overlapping periods are resolved automatically rather than silently double-counting.
Lookup runs in priority order: country and period, then country and default, then global period, then global default. The most specific rule that covers the order date wins, which means you can add a narrow exception without disturbing anything else.
The most common costing error is not a typo. It is recording the supplier's price and stopping there, when inbound freight, duty and customs brokerage were also paid to get that unit into the warehouse.
On imported goods the gap is routinely fifteen to thirty percent of the invoice, and it is uneven across a catalogue — heavier and bulkier items absorb more inbound freight per unit. Leaving it out does not just understate cost overall, it distorts the ranking between products, which is the thing the ranking exists to answer.
Divide each shipment's freight and duty across the units in it and add that to the per-unit figure before entering it. If your inbound costs are lumpy, cost periods let you attach a different landed figure to each shipment window.
A product shows implausibly high margin. It almost always has no cost and no default margin reaching it, or it is a bundle with no component definition, so its COGS is zero and its revenue is pure profit.
Import skipped most of the file. Matching is on SKU, exactly. Blank SKUs in Shopify, a leading space, or a case difference all miss. Export first and fill the file you were given rather than building one from your supplier's spreadsheet.
Historical orders did not change after a cost edit. New costs apply to new orders immediately; existing orders keep their stored values until reprocessed, so the change you made is real but has not yet been pushed backwards.
One variant is dragging a product down. Expand the product. A parent showing a healthy blended margin regularly contains one variant losing money on every unit, and the parent row is the last place that will tell you.
Set costs at variant level — Size M and Size XL rarely cost the same, and a product-level average hides the difference
Multi-variant products show “Varies” and expand in place into individually editable variant rows
Orders with missing COGS carry a warning badge in the orders list, and the dashboard banners it
Future orders use new costs immediately; historical orders keep their stored values until reprocessed
Bundle COGS sums component cost × quantity, one level deep — currently available via the API only
sku,cost_per_unit
PRO-JERSEY-BLUE-M,28.75
PRO-JERSEY-BLUE-L,28.75
PRO-JERSEY-WHITE-M,28.75
CAP-CLASSIC,12.40
TEE-STANDARD-S,19.50
TEE-STANDARD-M,19.50
SAMPLE-PACK,32.80
Two columns required: sku and cost_per_unit. SKU must match your Shopify variant SKU exactly.
They are costed with your default margin and shown as an estimate rather than left blank, so the ladder still reconciles. Once you enter a real cost, reprocessing those orders replaces the estimate with the exact figure.
On the variant. Sizes and colours frequently differ in both supplier price and shipping weight, and a product-level average hides the variant that loses money on every unit. Multi-variant products show “Varies” and expand into individually editable rows.
Not unless you want it to. Costs can carry an effective_from and effective_to date, so a price rise applies from the date it took effect and historical orders keep the cost that was true when they shipped.
Costs are matched by SKU against your Shopify variant SKUs, exactly and case-sensitively. Rows skip when the SKU is blank in Shopify, differs by whitespace or case, or belongs to a product that has since been deleted. The validation report names every skipped row.
Define it as a list of component products with quantities and its cost is the sum of component cost multiplied by quantity. Bundles are one level deep — a component cannot itself be a bundle. This is currently API-only; the interface for it is not built yet.