Contribution margin
What CM1 and CM2 mean in CCC, the signed chain that produces them, and why a component is added rather than subtracted.
Contribution margin is what remains of an order's revenue after the costs that vary with it. CCC computes it per transaction and aggregates it per sales order. It is the number most CCC screens are ultimately about, and the internal shorthand for the whole domain is CM2.
This page explains the chain. Where the numbers come from is calculation rules; when they are refreshed is data freshness and history.
The chain
Four account groups of type contribution feed it, in this order:
| Group | Holds |
|---|---|
revenues | What the order brought in |
cogs | Cost of goods sold |
voc | Variable operational costs: the cost of handling and fulfilling the order |
vmc | Variable marketing costs: the cost of acquiring the order |
From those:
gp = revenues + cogs
cm1 = gp + voc
cm2 = cm1 + vmc
pm = 81100 + 71100
Every component is added with its stored sign. The engine does not negate costs automatically. Positive costs occur in the shipping and payment seeds. See dates, currency and signs for a complete signed example and how presentation inversion changes its reading.
gp is gross profit. cm1 is gross profit after variable operational costs. cm2 is cm1 after variable marketing costs, and is the figure the platform is named for.
What belongs in VOC and what belongs in VMC
The dividing line is operational against marketing: did the cost arise because the order had to be handled, or because the order had to be won? The accounts in each group are the definition, and they are maintained at /configuration/accounts.
VOC is operational. Shipping, variable payment costs, packaging and import duties on sales; the things that go wrong in fulfilment, which are warranty costs, lost parcels, missing items, damaged items, fraudulent payment costs and write-downs on collectibles; transfers to the provision for obsolescent stock; the direct personnel costs of customer experience, the warehouse and engraving; age checks on an order; and other costs of sales.
VMC is marketing. SEA costs and marketplace commission: what was paid to get the order in the first place.
So the same euro can land in either group depending on why it was spent, and the account a rule books to is what decides. A rule pointed at the wrong account moves the cost between CM1 and CM2 without changing CM2 itself, which is why a CM1 that looks wrong while CM2 looks right can indicate an account-group mapping to investigate.
pm is a separate pair of general-ledger columns, 81100 and 71100, carried alongside the chain rather than derived from it. They are hardcoded account numbers, not an account group.
Where each figure lives
Rules write their output into one cm_impact_<group> table per contribution group. Those are then aggregated per order into cm_impact_salesorders, which carries revenues, cogs, voc, vmc, pm, gp, cm1, cm2, the two ledger columns, and a processed flag that acts as its own recalculation queue.
The same four formulas are computed in two places: for realised margin, and again for expected margin from synthetic downstream transactions. They are identical in both, so a difference between a realised and an expected figure is a difference in inputs, never in arithmetic.
Reading a figure correctly
Check the sign convention before calling a figure a loss. Stored CM2 and an inverted display have opposite signs. An order can legitimately cost more than it earned: a heavily discounted item, an expensive shipment, a return that reversed the revenue but not all of the costs.
The order's figures move after the order does. Margin is produced by rules that can be edited and re-run, and by downstream transactions that arrive later than the order itself. A figure read today and the same figure read next week can differ without anything being wrong.
Screen defaults hide history. The filtered transaction screens default to a window of the last three months. A figure that looks absent is often outside the window rather than missing.
Related
- Calculation rules, which produce every component
- Expected margin, the forward-looking twin
- Dates, currency and signs
- Sales orders
- The account groups themselves:
/configuration/accountgroups
Evidence
Formulas read from app/models/impact.php:88-91 and app/models/expectedimpact.php:120-123, which agree. Group names and their contribution type from config/reference/accountgroups.json. Aggregation and the processed flag from app/jobs/calculate/impactsalesorders.php. The operational-against-marketing split is from the data owner; the accounts listed for each group are the rows of accounts joined to accountgroups on technical of voc and vmc, read from the local database and visible at /configuration/accounts. Basis: code-checked against the current implementation.
- Type
concept - Status
active - Updated
2026-09-14 - Created
2026-09-13