Description
WooCommerce ships the cost data. It does not ship the report.
With the Cost of Goods Sold feature enabled, WooCommerce stores a cost for each product and stamps a cost snapshot onto every order line as the order is placed. Then it stops. WooCommerce’s own documentation states that Cost of Goods Sold does not integrate with WooCommerce Analytics, and the request to build profit reporting on top of it was closed as not planned.
Profit Margin Reports for WooCommerce is that missing report. It reads the cost data WooCommerce already holds and turns it into gross profit, margin, per-product profitability and a per-order profit figure. Nothing is sent anywhere, nothing is rewritten, and no number is presented as more certain than the data behind it.
What you get
- Profit summary for any date range: net revenue, cost of goods, gross profit and gross margin, with the previous period beside it for comparison.
- A daily series of revenue, cost and gross profit, so you see the trend instead of one number.
- Product and variation profitability, sortable by gross profit, net revenue, cost, units sold or margin, so the products quietly losing money stop being invisible.
- A profit column on the orders list, taken from the cost snapshot stored on that order’s own line items.
- Coverage shown next to every total. Each figure tells you how many of the order lines behind it actually carried cost data. A profit number built from 40 percent of your lines is labelled as such rather than presented as fact.
- An optional estimated mode that fills lines with no cost snapshot using the product’s current cost. Estimates are labelled everywhere they appear and are never silently blended into actual figures.
- A cost backfill assistant for entering product costs in bulk, either as a percentage of the regular price or by importing a CSV of your own costs. Both show you exactly what would change before anything is written. When it writes a variation cost it also clears WooCommerce’s per-variation “additive” flag, so a stale flag left behind by an importer cannot silently double that variation’s cost.
How these numbers relate to Analytics
Reports honour the same excluded order statuses as WooCommerce Analytics, and refunds are netted the way Analytics nets them: a refund reduces revenue, units and cost on the date the refund was issued.
Every figure is keyed on the order creation date, because the WooCommerce line-item table these reports are built from stores no other date. WooCommerce Analytics defaults its own screens to the paid date. On most stores those are the same day and the totals agree, but on a store that takes payment later than the order is placed, such as bank transfer or invoicing, the two will legitimately disagree about which day a sale lands on.
Read this before you install
A profit report is only ever as good as the cost data underneath it. Here is exactly what this plugin can and cannot know.
- It reports on cost data, it does not invent it. If a product has no cost entered, there is no profit to calculate for it.
- Orders placed before you enabled Cost of Goods Sold have no cost at all. WooCommerce writes the cost onto the order line as the order is created, so entering costs later does not travel backwards. Historical orders show as uncovered. Estimated mode applies today’s cost to yesterday’s order, which is useful for a rough view but is an estimate, not history.
- A cost of exactly zero is indistinguishable from no cost. WooCommerce stores a zero cost by deleting the value, so nothing downstream can tell a real zero from a blank. This plugin never reports a 100 percent margin for a line with no cost: such lines are counted as uncovered and excluded from margin.
- WooCommerce Analytics must be enabled. The reports read its lookup tables. The plugin also warns you when WooCommerce has orders it has not yet imported into them, because an incomplete import means an incomplete report.
- This release queries your order tables live. There is no separate pre-aggregated profit table yet. Every report is calculated when you load it, so on a store with a very large order history a long date range can be slow.
- One currency at a time. If your orders span more than one currency the plugin tells you, rather than adding different currencies together into a meaningless total.
Requirements
- WooCommerce 10.3 or newer.
- The Cost of Goods Sold feature enabled at WooCommerce > Settings > Advanced > Features.
- WooCommerce Analytics enabled.
- PHP 7.4 or newer, WordPress 6.7 or newer.
- Works with High-Performance Order Storage and with legacy post-based order storage.
Privacy
This plugin makes no external HTTP requests and sends no data anywhere, to its author or to anyone else.
There is no telemetry, no usage tracking, no analytics beacon, no phone-home on activation or deactivation, no remote licence check, and no third-party service of any kind. Every figure it shows is calculated from your own database on your own server, and nothing about your store, your orders, your costs or your customers leaves it.
It sets no cookies, registers no front-end assets and adds nothing to the customer-facing side of your store. It is an admin-only reporting screen.
Source and build
The compiled files in build/ are generated from the human-readable source in src/, which ships with the plugin. To rebuild them:
npm ci && npm run build
This uses @wordpress/scripts and requires Node 20 or newer. package.json and package-lock.json ship too, so the build is reproducible from the released files alone.
The build also compiles in nine ISC-licensed d3 packages: d3-array, d3-color, d3-format, d3-interpolate, d3-path, d3-scale, d3-shape, d3-time, and internmap. Their full licence text and copyright notices ship with the plugin in build/index.js.LICENSE.txt, generated at build time from each package’s own licence file.
Screenshots




Installation
- Install and activate WooCommerce 10.3 or newer.
- Go to WooCommerce > Settings > Advanced > Features and enable Cost of Goods Sold. Until this is on, WooCommerce records no costs at all and every report will be empty.
- Install this plugin from Plugins > Add New, or upload the plugin folder to
/wp-content/plugins/, then activate it. - Enter costs for your products. The field is Cost of goods on the Product data > General panel. To fill in many products at once, use the backfill assistant on the plugin’s own screen.
- Open WooCommerce > Profit to view your reports.
Only orders created after step 2 carry a cost snapshot, so expect the report to start filling in from that point forward. Use estimated mode if you need an approximate picture of the period before that.
FAQ
-
Why are my reports empty?
-
Work down this list, in order. The plugin’s own status panel checks all of these and tells you which one is failing.
- The Cost of Goods Sold feature is off. It is off by default in WooCommerce. While it is off, every cost value WooCommerce returns is zero and nothing is recorded. Enable it at WooCommerce > Settings > Advanced > Features.
- WooCommerce Analytics is off. The reports read the Analytics lookup tables, so with Analytics disabled there is nothing to query.
- Your products have no cost entered. Set Cost of goods on each product, or use the backfill assistant.
- Your orders predate the feature being enabled. See the next question.
- WooCommerce has orders waiting to be imported into its Analytics tables. Run the Analytics import from WooCommerce > Status > Tools and let it finish.
-
Why do my old orders show no cost?
-
Because there is no cost stored on them. WooCommerce copies a product’s cost onto the order line at the moment the order is created, which is what makes historical profit accurate even after you change a supplier price. The flip side is that orders created before the Cost of Goods Sold feature was switched on have no cost snapshot at all, and entering product costs today does not retroactively write costs onto those old orders. No plugin can recover data WooCommerce never wrote.
Two things help. Coverage reporting tells you honestly how much of a period is uncosted, and estimated mode applies each product’s current cost to those uncosted lines so you get an approximate figure. An estimate is always labelled as an estimate.
-
How does the CSV cost import work?
-
Upload a CSV containing a product identifier and a cost, then map which column is which. The plugin shows you every row it matched and every row it could not, and writes nothing until you confirm.
The uploaded file is validated by content rather than by its name, stored under a generated filename so nothing you upload can choose where it lands, and deleted in the same request that parses it. Costs are written through WooCommerce’s own product API, never by direct database writes. A template CSV of your existing products can be downloaded from the same screen; values in it are escaped so a product name or SKU beginning with an equals sign cannot execute as a formula when the file is opened in a spreadsheet.
-
Does this plugin send my data anywhere?
-
No. It makes no external HTTP requests of any kind. No telemetry, no tracking, no remote licence check, no third-party services. All calculation happens in your own database on your own server. See the Privacy section above.
-
Does it work with HPOS?
-
Yes. The plugin declares compatibility with High-Performance Order Storage and reads order data correctly under both HPOS and legacy post-based storage. It also declares compatibility with the Cost of Goods Sold feature itself.
-
Does it change my cost data?
-
No. Reporting is strictly read-only: it queries WooCommerce’s tables and writes nothing back to your products or orders.
The single exception is the backfill assistant, which exists specifically to write product costs, and only does so for the products you select, after you confirm, and using WooCommerce’s own product API rather than direct database writes. It never touches costs already recorded on existing orders, because those are a historical record.
-
Do the numbers match WooCommerce Analytics?
-
Usually, and where they differ there is a reason worth knowing.
The plugin honours the same excluded order statuses configured in your Analytics settings and nets refunds the same way. The difference is the date. These reports key every figure on the order creation date, because the WooCommerce line-item table they read stores no other date, while Analytics defaults to the paid date. If your store is paid at checkout those are the same day and the totals agree. If payment can arrive days after the order, the same sale can land on different days in the two reports.
-
How are refunds handled?
-
WooCommerce writes a refund as its own negative line, carrying negative quantity and a negative cost, dated on the day the refund was issued. Reports sum those lines along with everything else, so a refund reduces revenue, units and cost on the refund date. The refunded order is not retrospectively rewritten.
-
What does the coverage percentage mean?
-
It is the share of order lines in the current report that had a cost recorded. At 100 percent, every line was costed and the profit figure is complete. At 60 percent, four lines in ten contributed revenue but no cost, so the true cost is higher and the true profit lower than shown. Coverage is displayed rather than hidden precisely so that a partly costed report is never mistaken for a complete one.
-
Which orders count, and who can see the reports?
-
The same orders WooCommerce Analytics counts: everything except the statuses excluded in your Analytics settings, plus draft, auto-draft and trashed orders, which are never counted because they are not sales. Developers can adjust this with the
profit_margin_reports_report_statusesfilter.Anyone with the
view_woocommerce_reportscapability, which by default means shop managers and administrators. Writing costs through the backfill assistant additionally requiresmanage_woocommerce. -
Is a very large store going to be slow?
-
Possibly, in this release. Reports are calculated live against your order tables on each load, with a short-lived cache. Stores with hundreds of thousands of orders may find long date ranges take a while. Pre-aggregation is planned.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“Profit Margin Reports for WooCommerce” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “Profit Margin Reports for WooCommerce” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
0.1.1
Review feedback from the WordPress.org plugin team.
- The orders-list column stylesheet is now enqueued through wp_add_inline_style() on a registered handle rather than printed as an inline style tag.
- Tested against WordPress 7.1.
0.1.0
Initial release.
- Profit summary with net revenue, cost of goods, gross profit, gross margin and previous-period comparison.
- Daily revenue, cost and gross profit series.
- Product and variation profitability report with sorting and per-row cost coverage.
- Profit column on the WooCommerce orders list.
- Cost coverage reporting on every total, so uncosted lines are never mistaken for zero-cost lines.
- Optional estimated mode using current product costs for lines with no cost snapshot.
- Cost backfill assistant: set costs as a percentage of the regular price, or import them from a CSV, both with a preview before anything is written.
- Variation costs are written as absolute values, clearing WooCommerce’s per-variation additive flag so a stale flag cannot double a cost.
- Status panel reporting whether Cost of Goods Sold, Analytics, order storage and Analytics imports are in a state that allows accurate reporting.
- Compatible with High-Performance Order Storage and with the Cost of Goods Sold feature.
