Generate a financial statement package
Prerequisites
- A working Rust toolchain (see How to install the development toolchain)
- A checkout of the three accounting crates — the engine library, this deployment's binary crate, and the shared renderer
- Read access to the accounting data repository the binary crate resolves at startup
- Write permission on the output directory, or
ACCOUNTING_OUTPUT_DIRset to somewhere you can write
No services need to be running. All three crates declare zero third-party dependencies, deliberately, so a first build compiles the workspace's own code and downloads nothing.
One prerequisite cannot be satisfied by configuration, and it is worth knowing before the first run: the input data repository's location is compiled into the binary as an absolute path. There is no --data-dir, no positional argument, and no environment variable that moves it. A checkout whose data repository sits somewhere else fails at the first file read, naming a directory that is not yours. Only the output location is redirectable.
Purpose
Render one entity's consolidated financial statement package — Statement of Financial Position, Statement of Income (Loss), Statement of Changes in Unitholders' Equity, Statement of Cash Flows, and the Notes — as HTML and PDF, for one fiscal year and one period.
Nothing in the package is stored between runs. Every figure is recomputed from the journal CSVs each time: journals fold into per-account ledgers, ledgers fold into a trial balance, the trial balance consolidates across the group, and the consolidated lines are picked up by the statement layout. A rendered statement is a view of the journals as they stand at that moment, not a saved report that can drift from them.
For the ledger design underneath, see tool-accounting — double-entry ledger and audit-ready financial statements.
Procedure
1. Work from the binary crate's own directory
These crates are not a Cargo workspace. They are three sibling packages joined by path dependencies, each with its own lockfile — a deliberate choice, since the crate holding real financial data is meant to be handed to a reviewer in isolation. There is no workspace root to run cargo run -p from, which is the one thing an engineer arriving from Generate a construction cost estimate report will reach for and not find.
2. Confirm the inputs
The run reads six things. Four are master files at the root of the data repository, one is a directory of journals, and one is an event register.
| Input | What it carries |
|---|---|
accounts.csv |
The chart of accounts, one row per entity-and-account |
entities.csv |
The entity registry — legal name, and the rest of each entity's standing facts |
consolidation.csv |
Which entities consolidate into the reporting entity, and from when |
periods.csv |
Period end and completion date per entity, fiscal year, and period |
journals/<year>/*.csv |
Every posted transaction for that fiscal year |
register/<year>/events.csv |
The disclosable-event register the subsequent-events note derives from |
Three of these have schemas the loaders check for exact equality against their own header constants — a file whose columns have drifted is refused with both the found and the wanted header printed, rather than parsed on a best-effort basis.
accounts.csv, nine columns:
entity_code,account_code,ledger_account,statement,periods,sign,posting_tag,sourced,notes
periods.csv, six columns:
entity,fiscal_year,period,period_end_date,completion_date,lock_status
Each journal file, seventeen columns:
entity_code,account_code,fiscal_year,period,txn_date,counterparty_type,counterparty_id,description,ref_no,ref_source,subtotal_cad,gst_number,gst_amount,currency_code,amount_foreign,amount_cad,invoice_number
Two of these fields behave less simply than their names suggest:
signin the chart of accounts takes+1,-1,TBD, or blank.TBDand blank are not defaults — they mean the account's sign convention has not been confirmed, and every such account is excluded from every computed figure in the package and reported by name in the run's own summary. An unconfirmed sign is a visible gap, never a guess.periodon a journal row is always a disjoint quarter. Year-to-date is a property of the statement, assembled by summing quarters at report time; it is never a value a journal row is allowed to carry.
Journal files are discovered by filename prefix — <entity>_<account>_<year>_<quarter>_*.csv — and the quarter in the filename is what decides whether a file is in scope for the period being rendered. The filename duplicates what is inside the file on purpose, so the two can be cross-checked.
3. Choose a year and a period
The complete flag set is --year YYYY, --period Q1|Q2|Q3|YE, and -h/--help. Anything else is rejected by name with the usage line. The period argument is trimmed and upper-cased, so q1 works. The -- separator is required: without it, cargo consumes the flags itself.
All four periods render. YE is the annual package with a single money column. Q1 is a condensed interim package with two — the current quarter beside a prior-year comparative. Q2 and Q3 are condensed interim packages whose flow statements carry four money columns: a discrete three-month window and the year-to-date span, each beside its own comparative. The discrete window is a second, complete pass of the same pipeline, not a subtraction of one cumulative figure from another, and it is subjected to the same guards described under Verification.
Pass both flags explicitly. Each has a default compiled in — the period defaults to the annual package, and the year defaults to whichever fiscal year this deployment was first built for. Relying on either means a command whose output depends on a constant you cannot see from the command line.
4. Read the console summary
Unlike the sibling construction reporting binary, which deliberately prints no money to the terminal, this one prints its headline figures. The intent is different: these are the numbers you compare against the rendered PDF to confirm you are looking at the run you just made.
EXAMPLE-LP-01 consolidated (EXAMPLE-LP-01 + EXAMPLE-TC-01 + EXAMPLE-TC-02) — FY2024 Q1: 26 account(s) computed, 2 skipped (sign unconfirmed), 3 note-only
BS: Cash 40,000 | AP 12,500 | Deficit (57,500) | Total equity (deficit) 27,500 | Total liabilities and equity 40,000
IS: Share-based comp 30,000 | Professional 18,000 | Advisory 9,000 | Bank 500 | Opex 57,500 | Income (loss) (57,500)
-> /tmp/statements-scratch/2024/EXAMPLE-LP-01_statements_Q1.html
-> /tmp/statements-scratch/2024/EXAMPLE-LP-01_statements_Q1.pdf
Every figure, entity code and path above is invented for this guide. The shape is real: whole dollars, thousands separators, negatives in parentheses, and a nil figure as an en dash — the same convention the rendered statements use, so the terminal and the page can be compared line for line without mental arithmetic.
Read the second and third counts on the first line before anything else. skipped (sign unconfirmed) is the number of accounts excluded from every figure in the package. A nonzero count does not invalidate the run — it means the package is knowingly incomplete in a way the document itself discloses.
Expected outcome
Two files in the output directory, which the run creates if it is absent:
| File | What it is |
|---|---|
<entity-code>_statements.html / .pdf |
The annual package |
<entity-code>_statements_Q1.html / .pdf |
An interim package, suffixed with its period key |
The annual document carries no suffix and every interim period does. That asymmetry is not an oversight — the annual document is referenced by that exact path elsewhere, and keeping it stable was the point.
ACCOUNTING_OUTPUT_DIR redirects the whole tree to somewhere else, with the fiscal year appended as a subdirectory. Use it. The default output location is inside a shared, version-tracked data repository, and a rendering pass you make to look at a PDF should not dirty a committed artifact or race another process writing the same two paths.
Verification
Most of the verification is done by the run itself, before anything is written. Three guards stand between a computed population and a rendered page, and each refuses outright rather than rendering with a warning:
The population must balance, or the imbalance must be attributable. The computed double-entry population is summed; a nonzero residual means it does not tie. If no account was excluded for an unconfirmed sign, that residual is an arithmetic defect with nothing to attribute it to, and the run refuses. If accounts were excluded — each named — then the residual is the measured consequence of an already-disclosed gap, and the run proceeds and discloses it on the face of the statement of financial position. Refusing in that second case would suppress the very document that makes the gap visible.
Every nonzero computed line must appear somewhere on the layout. Each statement caption declares which consolidated lines it consumes. Any line carrying real money that no caption ever claims is an orphan, and the run refuses, naming it and its balance. A statement that silently drops a figure is wrong, not merely incomplete.
The cash flow statement must tie to the computed cash line. The derived change in cash is compared against the period's own computed Cash balance. A mismatch refuses the render and prints both figures.
So a package that exists has already passed all three. What remains for you:
- Compare the console summary against the rendered page. They use the same format for the same reason; a mismatch means you are reading an older PDF.
- Read the skipped-account names. They are printed by the consolidation step. Each is a real open item in the chart of accounts, not a tool limitation.
- Look for
POPULATION DOES NOT BALANCEin the console output. It appears only when the disclosed-gap branch above fires, and the same residual appears in the document. - Open the PDF. Comparative columns on an interim package print
n/a, never a dash — a dash on a filed statement asserts that the line is zero, and the comparative period here is unmeasured rather than zero. If a comparative column shows dashes, something has changed and the distinction has been lost.
What this task does not do
- It does not audit, certify, or file anything. It renders a document. Whether that document is filed, reviewed, or relied on is entirely outside this command.
- It does not compute opening balances. Opening positions are not yet sourced, so every balance-sheet figure is activity measured from a zero opening. The rendered document says so in a visible note immediately after the balance-sheet table. The income statement is flow-only and carries no such caveat, because none applies to it. This means the balance sheet is a real template with provisional figures — treat it as one.
- It does not compute the comparative period. Comparative columns are rendered because the filed interim statements carry them, and every figure cell in them reads
n/a. The shape is real; the period behind it is unmeasured. - It does not render any other entity's statements. Sibling binaries in the same crate render the general partner's standalone package and the subsidiary packages. Each takes its own arguments and is a different task.
- It does not render the management discussion and analysis. That is a separate binary.
- It does not print a trial balance or ledger detail. The crate's default binary does that, and it is worth running when a statement figure surprises you.
- It writes no journal entries. Against the data repository this command is strictly read-only. Its only writes are the two output files.
Edge cases
- An unrecognized
--periodvalue is rejected by name, with the usage line, exit status 1. Nothing is computed. --yearwith a missing or non-numeric value fails the same way. So does any unrecognized argument — there are no positional arguments to fall through to.- A missing
periods.csvrow for the entity, fiscal year, and period you asked for is a hard error namingperiods.csv. The subsequent-events window cannot be classified without a period end and a completion date, and the run will not guess them. This is the most common first failure when rendering a period for the first time. - A missing event register for the following year is not an error. The subsequent-events window opens the day after period end, so qualifying events live in the next year's register; the run loads it when it exists and proceeds without it when it does not.
- A header that has drifted — in the chart of accounts, a journal file, or the period registry — refuses the run and prints the found header beside the wanted one. Repair the file rather than working around it.
- A row with the wrong field count is not skipped with a warning. It aborts the run, naming the file and the row number.
- A money value with more than two decimal places is refused, not rounded. Sub-cent precision in a CSV is float noise from something upstream, and the error message says so. Quantize at the source.
- Adding two different currencies is refused. Conversion is a named, source-cited operation elsewhere; it never happens implicitly inside an addition.
- Output files are overwritten in place. No versioning, no timestamped directory, no prompt. Copy anything you need to keep, or point
ACCOUNTING_OUTPUT_DIRsomewhere disposable.
Rollback
Nothing to undo in the source data: the run reads the master files, the journals, and the register, and never writes to any of them. Its only writes are the two output files. Delete them, or re-run to replace them.
If you rendered into the default location and dirtied a tracked artifact, the fix is the ordinary one for a tracked file — restore it from version control, then re-run with ACCOUNTING_OUTPUT_DIR set.
Next steps
- Generate a construction cost estimate report — the sibling construction ledger's own reporting task, which shares this tool's renderer
- Generate a payroll register — the labour-hours register on the construction side of the same family
- Export structured data from the platform — moving rendered output or its source records somewhere else
See also
- tool-accounting — double-entry ledger and audit-ready financial statements — the double-entry design, the data model, and the audit-readiness posture these statements render
- tool-construction — construction cost, schedule, and quality ledger — the sibling development-and-construction ledger built on the same design
- The financial and construction tool family — a shared design across three products — where this tool sits among the financial and construction tools
Cite this record: /wiki/generate-a-financial-statement-package — revision 8fff5a87, last updated 1 September 2026.