Skip to main content

IBM Planning Analytics budget model with an offline rule evaluator

+----------------------------------------------------------------------+
|                       planning-analytics-model                       |
+----------------------------------------------------------------------+
|            IBM Planning Analytics budget model as source             |
+----------------------------------+-----------------------------------+
| DR  what it gives you            | CR  what it needs                 |
+----------------------------------+-----------------------------------+
| runs TM1 rules with no server    | PA model files in Git format      |
| validates model structure        | CSV data for cube loading         |
| prints a P and L report          | -                                 |
+----------------------------------+-----------------------------------+

The PaciolisCube budget model. This repository was named PaciolisCube until September 2026; the old URL redirects.

tests Codecov CodeQL Snyk SonarCloud Codacy code quality
OpenSSF Scorecard OpenSSF Best Practices SLSA 3 VirusTotal
PyPI python licence: Apache-2.0

An IBM Planning Analytics budgeting model published as source, with an offline engine that computes it and a test suite that asserts the answers.

Package lifecycle: published. Version 0.3.0 is on PyPI as pacioliscube; the model and its engine also run from a clone of this repository.

Planning Analytics models usually live inside a server. You can read a rule file on GitHub, but you cannot run it, and nothing tells you whether a change to it broke a number. This repository holds a complete driver based budget for a fictional Australian mining services group in the source form of IBM's TM1 source specification, which the Planning Analytics Git integration reads, plus a Python engine that parses the rules and evaluates them with no TM1 server anywhere. Continuous integration recomputes the whole model on every push and fails if a figure moves, and it loads the model with tm1gitpy, an independent TM1 Git implementation, to check that both readers see the same model.

On this page:

What is here

  • model/ is the model: a tm1project.json manifest, 13 dimensions, 5 cubes, 4 rule files and 8 TurboIntegrator processes. Rules are plain .rules text beside the cube JSON, scripts are plain .ti text beside the process JSON with a #region block for each of the Prolog, Metadata, Data and Epilog procedures, and cubes name their dimensions as Dimensions('X') references, as the specification's examples show.
  • pacioliscube/ is the engine: a rule parser, a structural validator, a cell store with weighted consolidation, a CSV loader and a command line.
  • examples/ contains fictional entities, commercial rates and volumes, plus dated statutory rates and thresholds cited in docs/model-assumptions.md.
  • docs/model-assumptions.md traces every statutory figure to its source, and says for each one whether it was read from the legislation or confirmed against the administering authority's own page.

Install

python -m pip install pacioliscube

Or from a clone, which also gives you the model files and the tests:

git clone https://github.com/ryanduguid/planning-analytics-model.git
cd planning-analytics-model
python -m pip install .

The runtime imports nothing outside the Python standard library. From a clone:

uv run --locked --extra dev pytest -q

Use

Run these examples from the root of the source clone above; they use the checked-in model/ and examples/ directories.

Check the model's structure:

pacioliscube validate model
0 errors, 0 warnings

Print a profit and loss from the shipped data:

pacioliscube report model --data examples --year FY2026-27 --version Budget
Profit and loss for FY2026-27, Budget
PnL at FY, Group, All Cost Centres

Revenue             52,764,000
Direct Costs      (14,884,800)
Gross Margin        37,879,200
Employment Costs  (11,428,418)
Overheads          (6,384,000)
EBITDA              20,066,782
Depreciation       (1,382,143)
EBIT                18,684,640

Read one cell, consolidated or leaf:

pacioliscube calculate model --data examples --cell "PnL:FY2026-27,Budget,FY,Group,All Cost Centres,EBIT,Amount"

Each report line is rounded to whole dollars on its own, so a subtotal can sit a dollar away from the lines above it. calculate prints the unrounded figure.

That block is engine output. The independent arithmetic behind these figures is in native comparison, which derives 8 cells from the synthetic inputs without the engine, and in the calculation tests.

Explain the inputs and arithmetic behind a cell:

pacioliscube explain model --data examples --cell "PnL:FY2026-27,Budget,Jul,CivilCo,Earthworks,Contract Revenue,Amount"

explain prints JSON with the requested cells and a breakdown of their dependencies. Each cell records its exact value and whether it came from an input, an unwritten cell's default zero, a rule or a weighted consolidation. Rules include their model-relative file and line, input cell references, and the arithmetic and comparisons actually evaluated. Consolidations list each child value, weight and contribution. Shared cells appear once; every weighted path still contributes to its parent.

Amounts, operands and weights are decimal strings in JSON. Comparisons remain booleans. An IF records the selected branch and evaluates only that branch. The command validates the whole model, then calculates only the requested cells and their dependencies. It does not check unrelated calculations. Repeat --cell to explain several cells in one run; a failed request emits no partial JSON. Large totals can have many dependencies, so start with a monthly cell.

This explains the offline model's calculations. It does not establish the correctness of source inputs, accounting treatment or agreement with native TM1.

For changes between two input snapshots, use the scenario comparison command.

The offline CSV loader refuses conflicting values for the same cell and names both rows. Repeated rows with equal decimal values remain valid, including when element names differ only in case or surrounding whitespace.

Exit codes

Code Meaning
0 Clean
1 A usage or input error, including a report that does not fit the model
2 The model does not load, or validation reports an error
3 A calculation failed

The model

Two entities, CivilCo (civil earthworks) and HaulCo (haulage), roll up to a Group. The year ends 30 June, so Period runs July to June. FY2025-26 is held as Actual and FY2026-27 as Budget.

Four cubes feed a fifth. Drivers holds statutory and planning rates and has no rules at all. Workforce turns headcount and pay rates into base pay, superannuation and payroll tax. Revenue turns billable hours and charge rates into revenue, and litres into fuel cost. Capex spreads fleet additions over each asset class life. All 3 feed PnL, which carries the statement from revenue down to EBIT.

No rule file holds a statutory rate. Every one is read from the Drivers cube with DB(), so changing the superannuation guarantee percentage means editing a CSV, not a rule. A test fails the build if a number in any rule or script carries a value the driver file ships.

What the tests actually check

The suite computes the model and asserts figures, rather than only checking that the source parses. Each expected number is written out longhand in the test, so a reviewer can follow the arithmetic without trusting the engine:

  • Monthly base pay is headcount times the annual rate over 12.
  • Superannuation is capped at the maximum contribution base, and the cap is proved to bite by asserting the gap from the uncapped figure.
  • Each year caps at its own base, so correcting one year cannot pass silently.
  • Payroll tax is levied on pay grossed up by superannuation, not on pay alone.
  • The payroll tax threshold credit reaches the designated group employer and no other cost centre.
  • Depreciation is annual additions over the asset life in months, and an asset class with no life set charges nothing rather than raising.
  • EBIT equals EBITDA less depreciation at the Group, across the full year.
  • The PnL wages line equals Workforce base pay summed over roles.

Breaking any of those in the model source turns the suite red.

Native TM1 comparison defines 8 synthetic cells, their independent expected values and the evidence to retain. Native comparison is pending because no TM1 server was available; passing offline tests does not establish agreement with a native server.

What it does not do

  • It is not tax advice, and it is not a payroll or lodgement engine. The superannuation and payroll tax treatment here is a planning approximation.
  • It is not a maintained rate table. The statutory figures are sample data pinned at the date in docs/model-assumptions.md, and they go stale.
  • It is not a full set of accounts. No balance sheet, no cash flow, no income tax, no GST, no foreign currency, no intercompany elimination.
  • The engine covers the subset of the TM1 rules language this model uses. It is not a Planning Analytics reimplementation, and it will not run an arbitrary model.

Client data

No real data belongs in this repository. The .gitignore blocks the file names ledgers and payroll exports arrive under. The examples combine invented business data with dated, sourced statutory figures. See CONTRIBUTING.md.

Releases

The repository's GitHub Releases page is the canonical release history. A separate changelog is intentionally not maintained.

Author

Written by Ryan Duguid, a provisional member of Chartered Accountants ANZ, independently, in his own time and on his own equipment. Nothing here is the work of any employer, and no client data was used to build or test it.

Every statutory figure is traced to its source in docs/model-assumptions.md, which records what was read from the legislation, what was taken from the administering authority's own page, and who confirmed the figures that could not be fetched.

Licence

Apache-2.0. See LICENSE. If you redistribute this work or build on it, keep the attribution in NOTICE.

Metadata

Release files for pacioliscube 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pacioliscube 0.3.0
File Size Uploaded
pacioliscube-0.3.0.tar.gz 122.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pacioliscube 0.3.0
File Interpreter ABI Platform
pacioliscube-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 164.4 kB

Release files / pacioliscube-0.3.0.tar.gz

Download URL pacioliscube-0.3.0.tar.gz
Size 122.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e9f23444a58d0dfd9dd6f5efc72d877cbff72b8beef4ebea3c3a2879f62efe91
BLAKE2b-256 checksum
How to use checksums
bd536beca9d3e348dd3378fdd092770b189ec32947ee5748e14f6916bc2ecdde
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / pacioliscube-0.3.0-py3-none-any.whl

Download URL pacioliscube-0.3.0-py3-none-any.whl
Size 42.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9feb183f57234193985c8f7d32bf80ce64c4a52248db07c6f461476a997ce37f
BLAKE2b-256 checksum
How to use checksums
e3f784f8567e584aa34cb7649bdcebee13a0d817c12f898fd8b4fe183f931567
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page