CostLedger is live: a dated cost ledger for Shopify

I’m Charles. I built CostLedger.

Shopify keeps one Cost per item. When a supplier price changes, that number overwrites what you paid before, and margin reports start using today’s cost on older sales. CostLedger keeps a dated receipt ledger and shows the moving-average math. You record what a batch cost by hand, CSV, or on Plus from a completed Shopify PO PDF. Margin reports use the cost from the sale date. It does not write back to Shopify.

It is not a receiving app. It does not print labels or send purchase orders.

If that sounds useful, the listing is CostLedger ‑ COGS & Margins - Keep a dated cost ledger so supplier price changes do... | Shopify App Store . I’d like people to try it and tell me here what’s missing. I’ll answer in the thread.

Charles, one thing about the listing copy before the product itself, because I think it costs you the exact buyer you want.

Shopify’s native profit report does not use today’s cost on older sales. The help page says profit is reported only for products and variants that had cost recorded at the time they were sold, and it calls Cost per item static data that makes the report relevant to a specific point in time. So the snapshot does happen. A merchant who already knows that reads your first two sentences and decides you have not looked closely, which is unfair to you, because the gap you are describing is real. It just sits somewhere else.

Where it is genuinely true is every third party margin tool, yours included. I went through the LineItem object in the Admin GraphQL reference and there is no cost field on it at all. quantity, sku, vendor, refundableQuantity are all there. unitCost is not. The only cost you can read through the API is InventoryItem.unitCost, which is the current value. So Shopify keeps a historical cost for its own report and exposes none of it, and anything living outside the admin has to join orders to today’s cost, which is the wrong number by construction.

The stronger version of your pitch is on that same help page and it is nastier than overwriting. The profit report only takes into consideration variants that had cost information at the time of sale, and orders without it go missing from the report rather than showing up at zero margin. Shopify even warns you will see net sales disagree between the sales report and the profit report. So the native number is not so much wrong as quietly incomplete, and nothing on the screen tells the merchant by how much. You can demo that on any store in under a minute, and it lands harder than a cost getting overwritten.

On timing, Stocky stops working after 31 August, six days out. Shopify’s own transition page describes your product for you. It calls the native field a single static Cost per item field, says that cost stays fixed when you receive a purchase order or a transfer, and says weighted average costing and landed cost allocation need third party apps. The same page says historical purchase orders cannot be imported and suppliers cannot be exported from Stocky at all. So there is a group of merchants whose cost history dies on 1 September with nowhere to carry it. You already take CSV, so a paste your Stocky export path that is live this week is worth more than anything you ship in October.

The thing I would want answered before trusting the numbers is backdated receipts. If I enter a batch today but date it three weeks ago, after sales have already drawn that inventory down, does the moving average recompute, and do margins you already reported move? Cost ledgers tend to get that wrong in one of two directions, and it decides whether anyone can close a month on it.

Charles, this is a solid app concept especially with Stocky shutting down in a few days. That alone makes this timely for a lot of merchants.

One thing I’d add to the feedback already shared: beyond the backdating question, consider how this handles partial receipts or split shipments on a single PO. Merchants often receive 50% of an order now and 50% later sometimes at a different cost if exchange rates or supplier pricing shifted between shipments. The moving average gets complicated quickly in those cases.

Also worth thinking about: multi-currency support. If a merchant buys from a supplier in USD but sells in GBP or EUR, the cost basis effectively changes with exchange rates on the receipt date, not just the supplier’s invoice price. That’s a layer most COGS tools miss entirely.

On the backdating question lumine raised what’s your current approach? Does the moving average recalculate retroactively, or are margins locked once reported? That’s the make-or-break question for anyone trying to close accurate monthly books.

Curious how you’re handling these scenarios.

Quick correction on my first post. I can’t edit it.

Shopify’s own profit report uses the cost recorded on the variant when it was sold. I overstated that. The gap I actually built for is different: Cost per item is one live value, ordinary Admin GraphQL LineItem has no historical per-line cost, and Shopify does not provide a dated, variant-level receipt ledger that third-party tools can query. CostLedger keeps that merchant-controlled receipt history and calculates dated cost basis from it. It does not write back to Shopify.

Thanks. You caught a real mistake in how I described the native profit report. I’ll fix the listing. I can’t edit the first post on this account, so the correction above is the thread fix.

Shopify says profit is reported only for variants that had cost recorded when they were sold. So no, today’s Cost per item does not rewrite older native profit reports. I was wrong there.

The LineItem point is right too. Ordinary Admin GraphQL LineItem has no historical per-line cost. ShopifyQL can expose order-level profitability data to apps with read_reports, but it still does not provide a dated, variant-level receipt ledger or per-line cost basis. CostLedger does not request read_reports; it keeps its own dated receipt history. The real pitch is a merchant-controlled variant cost ledger with the math shown, not that Shopify rewrites its own historical reports.

The missing-cost point is the better one. Shopify warns that profit reports omit variants that had no cost at sale, so net sales can disagree with the sales report. I’ll use that distinction.

On backdating: CostLedger sorts batches by receipt date and recomputes fresh reports from that timeline. Add a receipt today dated three weeks ago and newly run margin and monthly COGS for that period can change. There is no closed-period lock. Old daily trend points stay as they were. Fresh reports and later snapshots use the revised ledger. If a backdated receipt changes the earliest known basis, sales before the first receipt (CostLedger marks those estimated) can change too.

Your inventory-drawdown example hits another limit. The current average is cumulative across recorded receipts. It does not shrink the quantity pool for sales in between, so it is not a perpetual, on-hand-aware weighted average. I need to say that and the month-close limit up front.

The generic CSV importer is live. It is not a Stocky importer. It wants sku, quantity, and unit_cost. received_at is optional, though for migrated history you’d want it populated. A redacted Stocky sample would help me see if a direct map works. I won’t call it Stocky-compatible until that path is tested.

Good questions. Here’s what it actually does today.

Split shipments: you can record each arrival as its own dated batch, with its own quantity, unit cost, and reference. The timeline recalculates at each receipt. It does not manage partials against an open PO. The Plus PDF path only works on completed Shopify POs.

The average is cumulative across all recorded receipt quantities. It does not reduce the pool for sales between receipts, so it is not a perpetual, on-hand-aware weighted average yet.

Multi-currency the way you described is not supported. CostLedger works on USD and AUD shops. Every batch has to be in shop currency. It does not store supplier currency, the rate, or the rate source, and it does not convert at the receipt-date rate. GBP and EUR shops are not supported.

Backdating: fresh reports recompute from the dated ledger, so a backdated receipt can change margin and monthly COGS for that period. Closed periods are not locked. Old daily trend points stay put. Fresh reports and later snapshots use the revised ledger.

A proper month-close workflow needs explicit closed-period behavior and a clear treatment of backdated adjustments. If that’s your workflow, I’d like to hear how you expect corrections after close to behave.

Thanks for the detailed breakdown appreciate you taking the time.

On the month-close question: for merchants who need accurate monthly books, locked periods are important. If you backdate a receipt after the month is closed, it shouldn’t retroactively change reported COGS and margins for that period therwise historical reports keep shifting and nothing is ever final.

That said, some merchants are okay with adjustments flowing through as long as they can track why the change happened. Having both options would be ideal: lock older periods, but show a clear audit trail of any adjustments.

Out of curiosity is that something you’re planning to add, or are you keeping it simpler for now?"

Yeah, that’s the right month-close model.

CostLedger doesn’t lock periods today. Fresh reports recompute from the dated ledger, so a late receipt can move that month’s COGS and margin.

For now it stays simpler. One live ledger, no closed-period lock, no separate adjustment journal. Existing daily trend snapshots don’t get rewritten. Fresh reports and later snapshots use the revised history.

Period locking with an audit trail is the direction I’d take before calling this ready for formal month-close. No timeline on that yet.

If this is your workflow, how would you expect a post-close correction to land? Reopen the period, post into the current period, or show as a separate prior-period adjustment?