For years, recording what a product cost you in WooCommerce meant installing a plugin, each with its own private field. WooCommerce now has a Cost of Goods Sold (COGS) field of its own. It is a good foundation, and it is worth understanding exactly what it does, because one of its behaviours affects how far you can trust past figures.
A short history
The code arrived as an experimental feature in the WooCommerce 9.5 cycle and was developed behind a feature flag through 2025. In WooCommerce 10.3, released in October 2025, it moved out of experimental features and became a regular core feature. It is still off by default.
If your shop runs an older version, update before relying on it: the editing screens filled in over several releases.
How to enable it
Go to WooCommerce → Settings → Advanced → Features, tick Cost of Goods Sold, and save. Nothing about your existing products or orders changes when you do this; it makes the fields appear and starts the calculations.
WooCommerce’s own COGS documentation covers the switch and the screens. If you already use a cost plugin, note the warning there: with both active you can see two different cost totals on an order, and WooCommerce does not decide which one is right. Choose one source.
What it stores
On the product
A Cost of goods field appears on the General tab of the product editor. It holds one number: what one unit costs you. It is a static cost. There is no FIFO, no weighted average across purchase batches, and no separate field for freight or duty.
With the feature on you also get:
- a Cost column in the product list;
- a cost field in Quick Edit;
- a cost option in Bulk Edit, which sets every selected product to the same value;
- cost support in WooCommerce’s own product CSV exporter and importer;
- a cost of goods property on products in the REST API.
The field has no notion of VAT. Whether the number you type includes it is a convention you have to keep yourself.
On variable products
The parent product’s cost acts as a default, which every variation inherits. Any variation can override it with its own cost, and there is a bulk action to remove the individual costs so all variations fall back to the parent’s again. This makes the common case cheap: if all sizes of a shirt cost the same, enter it once on the parent.
On orders
On the order screen, each line gets a Cost column showing that line’s cost, the unit cost multiplied by the quantity, and the order gets a cost total next to its order total. Refund lines show the cost of the items refunded. The values are stored in the order item meta.
Where it stops
No profit reports
WooCommerce’s documentation is direct about this: the COGS feature does not integrate with WooCommerce Analytics. The cost is visible order by order, but no report adds it up into gross profit by product, by category or by month. Delivery and payment fees are not part of the feature either. To get profit you still need a spreadsheet or a plugin; the guide on WooCommerce profit reports lists what such a report should contain.
An unset cost counts as zero
A product with no cost entered is treated as costing nothing. WooCommerce’s documentation says the field defaults to zero if it is not set. Any profit you calculate on top of it will show those products at a 100% margin, with nothing to tell a real zero from a missing one.
Recalculating an order uses today’s cost
This is the behaviour that matters most for historical figures.
When an order is placed, WooCommerce works out each line’s cost from the product’s cost at that moment. Changing a product’s price or cost later does not, on its own, touch that order. So far, so good.
But the line cost is not frozen. Whenever an order’s totals are recalculated, WooCommerce works each line’s cost out again, from the product’s current cost multiplied by the quantity. You can see this in the order code on GitHub: the totals calculation calls the cost calculation for every item. The Recalculate button on the order screen does it; so can any plugin that recalculates totals, which is common for editing, shipping and invoicing plugins.
In practice: you sell a product in March at a cost of €8. In May the supplier raises it to €10 and you update the field. In June you add a line to that March order, or a plugin recalculates it. The March line now says €10. Nothing marks the change. Last quarter’s figure has moved, and the only way to notice is to have written the old one down.
For a shop that rarely edits old orders this may never come up. For any shop that wants to compare one period with another, it means the stored cost is a good default, not a record.
How Caltru Ledger builds on it
Caltru Ledger is a free plugin that treats the native field as the place costs live, rather than replacing it with a copy of its own. It switches the feature on when activated, because WooCommerce ignores the field while it is off, and every cost you enter stays in WooCommerce’s field if you ever remove the plugin.
What it adds is memory. It writes the cost of each order line into its own records the moment the line is created, from checkout, the admin screen, the REST API or an import, and never rewrites it. It then hands that recorded figure back to WooCommerce through WooCommerce’s own per-item cost filter, so when an order is recalculated, the order screen still shows what the line actually cost, not what the product costs today. The historical costs page explains the mechanism.
On top of that record it builds what the native feature leaves out: profit reports by period and product, net profit after delivery and payment fees, refunds dated by the refund, and a list of products with no cost ranked by how much profit each one is overstating, so a missing cost is never read as a zero. It also makes entering costs safer: costs can be entered including VAT and are normalised with each product’s own tax class when a sale is recorded, a blank Quick Edit field no longer clears a cost, and Bulk Edit gains percentage and relative changes. See entering costs.
What it does not change: costs are still one static figure per product, with no FIFO, weighted average or landed cost.
In short
- Enable it under WooCommerce → Settings → Advanced → Features.
- Enter costs on the product, or once on a variable product’s parent with overrides where variations differ.
- Treat an empty cost as unknown, not as free.
- Expect no profit reporting from WooCommerce itself.
- Remember that recalculating an order re-costs it at today’s prices, and record historical costs somewhere that does not move if past periods need to stay comparable.