Export a rollup per customer group
This guide sums one run's statements under the groups its projects belong to and writes one document per group beside the statements. The statements themselves are unchanged by it. What each document holds is in rollup.
Before you start
- Both databases reachable from the machine you run the CLI on: the engine's own and the Reporting API's. A rollup reads the membership from the registry when the export runs, so an export with
--rollupneeds the second one. - The id of the run to export, and the relation type to sum under,
member_oformanaged_by. The groups themselves are the meta-projects and partners the projects relate to (model a customer group). - One empty directory per export, or a path that does not exist yet.
tally-engineat the version the deployment runs, with its subcommands on the engine CLI page.- The engine settings page, which names the two variables this guide sets.
Write the group documents
Export both databases. An export without
--rollupreads the engine one alone, andTALLY_ENGINE_REPORTING_DB_URLis what makes the membership readable:shexport TALLY_ENGINE_DB_URL='postgres://tally:password@db.internal:5432/tally_engine?sslmode=require' export TALLY_ENGINE_REPORTING_DB_URL='postgres://tally_engine:password@db.internal:5432/tally_reporting?sslmode=require'Sum the run under the meta-projects its projects are members of. The rollup line stands beside the lines a plain export writes:
shtally-engine export --run <run id> --format json --out ./2026-07/groups --rollup member_oftextrun <run id> exported for 2026-07 as json into ./2026-07/groups wrote run.json and <n> statements wrote kickbacks.json with 0 kickbacks wrote <k> rollup documents over member_of--rollup managed_bysums under the partners that manage the projects instead, and--format csvwrites the table rather than the documents:shtally-engine export --run <run id> --format csv --out ./2026-07/partners --rollup managed_bytextrun <run id> exported for 2026-07 as csv into ./2026-07/partners wrote rated.csv with <n> rated records wrote kickbacks.csv with 0 kickbacks wrote rollup.csv with <m> members over managed_byrollup.csvcarries one row per member of every group, with the group on the row.
Check the result
One document per group stands beside the statements. A virtual project carries its platform as its cloud, so the meta-project
customer-alphais written torollup-meta%2Fcustomer-alpha.json:shls ./2026-07/groupstextkickbacks.json rollup-meta%2Fcustomer-alpha.json run.json statement-os-prod-eu1%2F9c4a1b2d3e4f5061728394a5b6c7d8e9.jsonrun.jsoncarries arollupmember naming every group document with its member count and its total:shjq .rollup ./2026-07/groups/run.jsonjson{ "relation_type": "member_of", "documents": [ { "file": "rollup-meta%2Fcustomer-alpha.json", "cloud": "meta", "project_id": "customer-alpha", "members": 3, "total": 412.77, "currency": "EUR" } ] }documentsis empty rather than absent where the rollup reached no group at all.A group's total equals the sum of the member totals it lists, to the cent:
shjq '{total, members: [.members[].total]}' \ ./2026-07/groups/rollup-meta%2Fcustomer-alpha.jsonjson{ "total": 412.77, "members": [128.45, 199.98, 84.34] }Two groups are not disjoint: a project is listed under every group it belongs to, and the totals of two groups add up to more than the run billed. Nothing is summed for a project the run billed under another project's statement, and the root's membership is what decides where those costs land.
Two exports of one finalized run differ where a relation was created or closed between them. The membership is read from the registry when the export runs rather than stored with the run.