MerchantFlowMerchantFlow Docs
Profit & Loss

COGS Accuracy - Effective Dates, Coverage, and Recompute

Keep MerchantFlow COGS accurate with effective-dated entries, cost history badges, manual override precedence, coverage metrics, refund treatment, and recompute behavior.

COGS Accuracy

Accurate COGS is about more than typing in a number: it means the right cost, on the right variant, effective from the right date, in the right currency. MerchantFlow resolves the cost for every order line from your cost history, applies precedence rules between manual and imported entries, and recalculates profit automatically whenever costs change. This guide explains those mechanics so your margins stay trustworthy - and so you can tell the difference between a data problem and the system working as designed.

How to Access

Most of what this guide covers lives on Profit > COGS: the per-SKU Cost History modal, the coverage indicators in the page header, and the source badges in the table. Coverage warnings also appear on the main dashboard and the P&L page, noted below.

Effective Dates: Add a New Entry, Never Edit the Old One

Every cost entry has an effective date, and MerchantFlow resolves each order against the cost history using the order's own order date (the date the order was placed - not the refund date, and not the reporting date). An order uses the most recent entry whose effective date is on or before the order date. Orders older than your first entry fall back to that earliest entry, so ancient orders still get a cost - the one exception being entries written by the supplier invoice import, which are deliberately forward-only and never backfill orders that predate them.

Note that this is independent of the Refund Cost Recognition setting below: even when refunded orders are recognised on the refund date, the cost card is still chosen by the order date.

The golden rule when a supplier changes prices: add a new entry dated at the change - do not edit the existing entry.

Worked timeline. Your unit cost was $4.00 from launch; on July 1 the supplier raised it to $5.00:

You doJune orders resolve toJuly orders resolve to
Add a new entry: $5.00 effective July 1$4.00 (correct)$5.00 (correct)
Edit the old $4.00 entry to $5.00$5.00 (history rewritten)$5.00

Editing the old entry silently rewrites every historical margin. Adding a dated entry preserves history and switches cost exactly at the change.

Note: Price Breaks - the quantity-tiered pricing ladder above the base unit cost - are the one deliberate exception to this rule. Saving the Price Breaks modal replaces the whole ladder in place, because a merchant thinks of it as one current list rather than a series of dated cards. The base (quantity-1) cost is unaffected and keeps ordinary effective-dated history as described here.

Scheduled Price Changes

An entry with a future effective date is inert until its date arrives, then activates automatically. Scheduled entries are listed in the Upcoming Price Changes panel on the COGS page. Use "Schedule Price Change" when your supplier announces pricing ahead of time - see COGS Management for the step-by-step flow.

Reading the Cost History Modal

Open Cost History on any SKU or variant to see its full timeline. Each entry carries a status:

  • Active - the entry currently used for calculations
  • "Active - override" - a manual entry that is active and takes precedence over newer auto-synced entries
  • Overridden - an auto-synced entry that is recorded but not used, because a manual entry outranks it
  • Scheduled - a future-dated entry waiting for its effective date

When a cost held steady across many daily syncs, the history collapses the run into one card showing the first date it was set and the last date it was seen, marked "unchanged" - so a stable cost reads as one line, not ninety.

Which Entry Wins: Source Precedence

Every entry records its source, shown as a badge in the COGS table:

BadgeMeaning
MANUAL OVERRIDEEntered by you in MerchantFlow. Takes precedence over auto-synced entries and is never overwritten by a sync
SHOPIFY / WOOCOMMERCEAuto-synced from your store's cost fields
CSVImported from a CSV or BeProfit file
INVOICE_IMPORTWritten by the supplier invoice import, where that early-access path is enabled
MIXED(Product row) variants have a mix of sources

There are two distinct protections, and it is worth keeping them apart:

  • Sync protection covers manual, CSV/BeProfit, and invoice-import entries. A Shopify or WooCommerce sync running in the default fill-in mode will never overwrite any of them; the synced value is recorded in history as Overridden instead. The only way a store sync replaces them is the explicit "Replace all costs with my store values" option - see Importing COGS. Sync protection is keyed on the base (quantity-1) cost, so a manually entered price break does not by itself block a store sync from writing the base cost.
  • Read-time precedence is narrower. When resolving an order, MerchantFlow first looks for a manual or invoice-import entry, and only if none applies does it consider entries from any other source - so a manual entry beats an auto-synced one even when the synced one is newer. CSV entries are sync-protected but do not jump the queue this way: among non-manual entries, the highest eligible quantity tier wins and the most recent effective date breaks ties.

Variant-Level Accuracy and Shared SKUs

Costs resolve down a variant -> SKU -> product identity chain, so within a given source tier a variant-level entry beats a SKU-level entry, which beats a product-level one. Variants of one product with different supplier costs should each carry their own entry.

Source tier is checked before specificity, not after. MerchantFlow sweeps the whole variant/SKU/product chain looking for a manual entry first, then sweeps it again for any source. A manual SKU-level cost therefore outranks an auto-synced variant-level cost - which is usually what you want, but is worth knowing if a store sync appears to be "ignored" on one variant.

The shared-SKU rule: when an order line has no variant identity and its SKU is shared across multiple variants with different costs, MerchantFlow deliberately counts that line as uncovered rather than guessing which variant's cost applies. Fabricating a cost would silently corrupt margins; a visible coverage gap tells you what to fix.

Recommendation: give every variant a unique SKU in your store. Unique SKUs eliminate ambiguity, make CSV imports precise, and keep coverage honest.

Coverage Metrics

Coverage tells you how much of your sales have a known cost:

  • Product coverage - share of active variants in your catalogue that carry a cost entry. This is the "Cost Coverage" KPI on the COGS page, and it looks at your catalogue, not your orders
  • Unit coverage - share of sold units whose order line resolved to a cost. This is the figure stored on each daily P&L snapshot (units covered / total units), and the one the dashboard warning uses
  • Revenue coverage - share of revenue with known cost; the most important one, since your best sellers dominate profit

Where warnings appear:

  • The main dashboard shows an amber pill when unit coverage drops below 90%: "{n}% of sold units have no cost data", with a tooltip warning that margin is likely overstated
  • The P&L page header shows a "COGS Coverage" percentage

Target above 90% revenue coverage. Below that, gross margin figures flatter you - uncovered units contribute revenue with zero cost.

Coverage can read below 100% even with a fully costed catalog: shared-SKU ambiguity (above) and refunded-but-never-fulfilled orders (below) both exclude units by design.

Refunds and COGS

A refunded order keeps its COGS only if the order was actually fulfilled - you paid for goods that shipped. A refunded order that was never fulfilled has its COGS excluded from the P&L entirely (the goods stayed in inventory). This is binary per order, so a partially refunded, unfulfilled order drops all of its COGS.

The Refund Cost Recognition setting in Settings > Financial Preferences controls when a refunded order's costs count:

  • "On the sale date" (default) - costs stay on the order date, lining up with the revenue they produced
  • "On the refund date" - costs move to the day the refund happened

This setting changes how stored figures are read - switching it never rewrites or recalculates data.

Currency Rules

Product cost (COGS) entries can be denominated in a currency other than your store's - useful when a supplier quotes in USD for a store that sells in EUR. The COGS add/edit forms, the bulk-rule modals, and the CSV importer's currency column all offer a "Costs are in" selector or per-row override, defaulting to your store currency. MerchantFlow converts a foreign-denominated entry at the exchange rate of each order's own date when calculating profit - not the entry's effective date - so one cost card can apply correctly to many orders spanning many daily rates.

Everything else stays in your store currency, with no per-entry override: percentage-of-price and target-margin bulk rules (they derive from tenant-currency sale prices), per-unit and per-order fulfillment rates, and country fulfilment rates, including their quantity tiers. See Fulfillment Costs and Country Fulfillment Rates.

Every cost entry carries an explicit currency, and it is never a guess. Costs you type in take the currency shown in the "Enter costs in" picker; if you save before the page has finished loading your store currency, the entry is recorded in your store currency rather than a placeholder. Store cost sync records Shopify costs in your Shopify shop currency and WooCommerce costs in your MerchantFlow store currency, without converting the number.

Legacy currency labels. Cost entries saved before the currency picker existed (August 2026) could carry a USD label even though you typed the number in your store currency. On a store that does not trade in USD, such an entry shows its value with the USD code beside it on the COGS page (for example $150.00 USD on an AUD store) and, because the label is treated as a real denomination, profit converts it at each order's daily rate - so COGS comes out higher than the number you entered. The number itself was never changed. If you see this, contact support: the entries are relabelled to the currency you entered them in, again without changing the numbers, and profit is recalculated. Entries created after the fix always carry the currency you chose.

One consequence worth knowing: orders placed in a different currency than your store currency are excluded from the P&L entirely - both their revenue and their costs. They are not converted. Margins stay internally consistent, but if part of your volume sells in another currency, your P&L understates total volume by that share.

Recompute Behavior: Why Pages May Briefly Disagree

Any cost change - adding, editing, or deleting an entry, importing a file, changing fulfillment rates - triggers an automatic rebuild of your profit history. How far back it rebuilds depends on what changed:

What you changedRebuild starts from
An ordinary cost entryThat entry's effective date
An entry that also carries a fulfilment rateYour oldest order - because the earliest configured rate backfills orders that predate it
A price-break ladderThe earliest effective date of any break row the save deleted
A country fulfilment rateYour oldest order, for the same reason as above

What you will observe:

  • The P&L page updates immediately for date ranges up to 90 days - within that window it computes COGS live from the current cost history rather than reading a stored snapshot
  • For longer ranges, only the trailing two tenant-local days (today and yesterday) are computed live; the rest is read from stored snapshots and updates once the rebuild lands
  • Product-level profit and per-order margins lag until the background rebuild finishes - minutes on most stores, longer on very large ones
  • Briefly, the products table and the P&L page can disagree. That is the rebuild in flight, not data loss - check back shortly

Handling Costs

A cost entry can carry a per-unit handling cost alongside the unit cost. Handling is folded into the COGS line of the P&L (not fulfillment): every sold unit books unit cost + handling.

Two sharp edges:

  • Handling can currently only be loaded via the BeProfit import (its handling_cost / handling / shipping_cost columns) - there is no handling input on the COGS page and no handling column in the generic CSV. See Importing COGS.

Note: Editing a cost manually (inline edit, bulk update, mass update) writes a fresh entry with handling reset to 0 for that SKU from that date. If you rely on imported handling costs, re-import them after bulk edits or fold handling into the unit cost instead.

Best Practices

1. Date Entries at the Change, Never Edit History

New supplier price - new entry with the change date. Reserve edits for correcting genuine data-entry mistakes, accepting that they rewrite history.

2. Watch Revenue Coverage Weekly

Keep revenue coverage above 90%. Start with your best sellers - covering the top 20% of products typically covers 80% of revenue.

3. Keep SKUs Unique per Variant

Unique SKUs remove ambiguity from cost resolution, imports, and coverage. Shared SKUs are the most common cause of "coverage will not reach 100%".

4. Expect Brief Lag After Edits

After a cost change, give the background rebuild a few minutes before comparing pages. Persistent disagreement after an hour is worth reporting; disagreement two minutes after an edit is the system working.

Troubleshooting

Coverage is lower than expected despite entries everywhere

Cause: Shared SKUs across variants (ambiguous lines count as uncovered) or refunded-never-fulfilled orders (COGS excluded by design, units still counted). Solution: Give variants unique SKUs and re-sync. The refund effect is correct behavior - those units genuinely carry no cost in the P&L.

The dashboard and product pages disagree after a cost edit

Cause: The P&L computes COGS live; product-level figures wait for the background rebuild. Solution: Wait a few minutes. If the gap persists beyond an hour on a normal-sized store, contact support at [email protected].

A Shopify sync did not update a cost

Cause: That SKU has a manual entry, which outranks synced values - the synced cost is recorded as Overridden. Solution: This is by design. To let store values win, use "Replace all costs with my store values" in the import modal - see Importing COGS.

Handling cost dropped to zero

Cause: A manual cost edit wrote a fresh entry with handling reset to 0. Solution: Re-import handling via the BeProfit tab, or include handling in the unit cost going forward.

Frequently Asked Questions

Will editing a cost rewrite my historical profit?

Editing an existing entry - yes, that is exactly what it does, back to the previous entry's boundary. Adding a new entry with an effective date - no, history before that date keeps the older cost. When in doubt, add rather than edit.

Why do refunded orders count against my coverage?

Refunded-never-fulfilled orders have their COGS deliberately excluded while their units still count in the denominator. It is a signal of refund volume, not missing data.

What does the "Reconstructed" badge on a product mean?

The product was deleted in your store, and MerchantFlow rebuilt it from order history so those historical sales stay attributable. Reconstructed products have no store cost to sync - set their cost manually so historical margins are accurate.

How are warranty or replacement orders handled?

Zero-revenue warranty orders still carry fulfillment cost. Settings > Financial Preferences > Warranty Order Detection lets MerchantFlow identify them (by zero revenue, SKU prefix, or specific products) and report what warranty fulfillment costs you. Their costs are categorized, not excluded.

Why is an order missing from the P&L entirely?

Most likely it was placed in a currency different from your store currency - such orders are excluded rather than converted, on both the revenue and cost side.

Does changing Refund Cost Recognition recalculate anything?

No. It is a read-time choice between two views of the same stored data - switching it is instant and reversible.


Last updated: September 9, 2026

Last updated on

On this page