Consumption contracts
The identity, date, unit, grain and freshness rules that make an aggregate over CCC data correct.
For anyone aggregating CCC data outside the application. Each rule here has produced a wrong number that looked right.
Identity
A customer is computed, not imported. It is a group of orders sharing identifying keys, rebuilt rather than patched.
- A customer id is a durable starting point, not fixed membership. Absorbed rows remain; follow
mergedintountil the live row. See merges and splits. - Historic figures move. A merge renumbers sequences and changes past new-customer counts. A report run in March and rerun in June can legitimately differ.
- If a figure must be stable, capture it when you publish it. CCC reconstructs from current understanding; it does not keep what was believed at the time.
Dates
Two bases, and they answer different questions.
| Basis | Used by |
|---|---|
createddate | Transaction listing date filters |
trandate | Customer sequence, rule activation and the impact listing |
| Accounting period | Ledger totals and income statement periods |
They can disagree about which order came first. Pick deliberately and state which you used.
createddatetime is subordinate and may be absent on older records.
Units and signs
- Columns carrying
_eur_are euro. - The engine preserves signs and adds components. Positive costs occur in the seeds; see the signed example.
- The interface may invert signs for readability. A query returns the stored convention.
ns_items.weightis in an unknown unit, with at least four distinct unit identifiers across the catalogue. Do not convert or compare weights.- Currency is converted per transaction, at the rate stored on that transaction. The
amount_eur_*columns are already euro and need no further conversion;exchangerateandamount_foreign_invatare there when you need the original. Do not apply a period average on top.
Grain, and the join that multiplies
An order has many lines, and many impacts. Joining an order to either and summing the order total counts it once per joined row.
| To aggregate | Join to |
|---|---|
| Order-level margin | The per-order impact table, one row per order |
| Line-level detail | The line tables, and do not carry order totals through |
This is the most common silent error in CCC consumption.
Freshness
- Margin moves after the order does, as downstream transactions arrive and rules re-run.
- A stale customer dimension leaves margin correct, so a margin figure being current says nothing about customer counts.
- The unauthenticated health endpoint reports per-step freshness. Key on its stable
jobfield, never on its numeric ids, which are reassigned on every deploy.
Absence
Three different things that all look like a gap:
- A filter excluded it.
- A step has not run yet.
- No source ever supplied that period.
Only the third is permanent. Object documents carry the provenance that distinguishes them.
Related
Evidence
Date bases from app/models/customers.php:1190-1192 and AGENTS.md, Models and database. Sign convention from app/models/impact.php:88-91. Weight unit and the health endpoint's unstable ids from AGENTS.md. Currency conversion from the exchangerate column stored per transaction. Basis: code-checked against the current implementation.
- Type
reference - Status
active - Updated
2026-09-14 - Created
2026-09-13