feat: canonical report set in QuickBooks-style presentation

Every report a CPA expects, laid out the way US small-business books
read (QuickBooks as the reference):

- Exports: books.export gains general_ledger (by account, running
  balances, splits), general_journal (the entry-ordered listing, which
  the CPA package used to mislabel "general ledger"; alias journal),
  members_equity, and trial_balance as of a date, in xlsx/csv/json, and
  PDF for all five statements (P&L, balance sheet, cash flow, trial
  balance, members' equity). books.trial_balance takes as_of.
- CPA package: every statement as PDF and XLSX, plus the general ledger
  and general journal, all for the fiscal year to date.
- Layout (ledger-import::report): multi-column statements (Total;
  Debit/Credit; one column per member plus Total), "$" on subtotals and
  totals, a rule above every sum (none over an empty section), a double
  rule under statement totals, zero as 0.00, negatives in parentheses
  with the closing paren hanging so decimal points align.
- Terms: "Net income (loss)"; for a multi-member LLC, "Members' equity",
  member contributions and distributions named in the cash flow, and the
  balance sheet splitting retained earnings (the Retained Earnings
  account plus earnings not yet closed from earlier years) from the
  fiscal year's net income (loss); the cash flow's "Adjustments to
  reconcile net income (loss) to net cash from operating activities" and
  "Net increase (decrease) in cash".
- PDF: Inter replaces the typst-assets serif fonts (bundled, SIL OFL 1.1,
  license in fonts/; from the official v4.1 release), centered title
  block, column-header row, full-width rules, footer "Accrual basis ·
  Prepared <date> · Journal seq N" with page numbers. Amounts on total
  rows stay regular weight so bold figures can't leave the decimal
  column. typst-assets dependency removed.
- XLSX: centered title block, header row, Excel accounting formats with
  "$" attached on sums, general ledger and general journal workbooks.
- Config: wanderbooks.toml [[company.members]] (name, share "50%" or
  "0.5", capital_account; shares must total 100%) and [books]
  accounting_basis (accrual | cash).
- Core: RETAINED_EARNINGS_CODE names the "3999" that period close uses.
- Fix: "today" is local time, not UTC; in US evenings UTC is tomorrow,
  so reports defaulting to "through today" included the next day and
  footers carried the wrong date.

Tests: layout (currency and rule placement, retained earnings vs. this
year's net income for a member LLC, footer text); exact template data;
deterministic PDFs; config parsing (members, shares, basis); stdio:
trial balance as of, February general ledger (beginning 1,004.00, splits,
ending 1,003.50), members' equity (contributions 1,000.00, net income
3.50, ending 1,003.50), members' equity PDF, the CPA package's ten files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-11 20:29:12 -07:00
1 parent 02b3b9c053
commit 8f623ba810
21 files changed
+1401 -543

No files matched your search

+34 -16
View File
@@ -27,7 +27,11 @@ layer.
`--data-dir`; override with `--books-toml PATH`): `[company] name` heads
every report, `[company] fiscal_year_start` ("MM-DD", default "01-01";
the year's end is derived, never stored) defines fiscal-year-to-date for the
CPA package, and `[books] receipt_threshold` sets the receipt policy unless
CPA package and the balance sheet's net income line, `[[company.members]]`
(`name`, `share` as "50%" or "0.5", `capital_account` code; shares must sum
to 100%) drives the statement of members' equity, `[books]
accounting_basis` ("accrual" or "cash", default accrual) labels report
footers, and `[books] receipt_threshold` sets the receipt policy unless
`config.json` sets `receipt_policy`.
## Transports
@@ -85,8 +89,8 @@ below.
- `books.get_entry { entry_id }` → full entry with lines.
- `books.search_entries { account?, date_from?, date_to?, text?, limit?, offset? }`
→ entries with lines, newest first. `limit` caps at 100.
- `books.trial_balance {}` → canonical JSON (same shape as
`reports::snapshot` trial-balance section).
- `books.trial_balance { as_of? }` → `as_of`, lines, totals, `balanced`
(the trial balance as of the date, default today).
- `books.balance_sheet { as_of? }` → totals (`assets`, `liabilities`,
`equity`, `current_earnings`, `balanced`) plus `asset_accounts`,
`liability_accounts`, `equity_accounts` (`{code, name, amount}`), as of
@@ -192,12 +196,27 @@ posting.
## Tools — export
- `books.export { format: "csv" | "json" | "xlsx" | "pdf" | "iif", report:
"journal" | "trial_balance" | "profit_and_loss" | "balance_sheet" |
"cash_flow", start?, end?, as_of?, cash_accounts?, financing_accounts? }` →
`{ content, content_encoding? }`. CPA-ready output. `xlsx` and `pdf` are
base64-encoded; `pdf` covers the three financial statements; `iif` is
journal-only (QuickBooks Desktop). Cash flow needs `cash_accounts`.
- `books.export { format, report, start?, end?, as_of?, cash_accounts?,
financing_accounts? }` → `{ content, content_encoding? }`.
- Reports: `profit_and_loss`, `balance_sheet`, `cash_flow` (needs
`cash_accounts`), `trial_balance`, `members_equity` (statement of
changes in members' equity; members from `wanderbooks.toml`),
`general_ledger` (by account: beginning balance, postings with running
balance and split, ending balance), `general_journal` (alias
`journal`: every entry, line by line, in date order).
- Formats: `pdf` and `xlsx` (base64) for the five statements; `xlsx`,
`csv`, `json` for every report; `iif` for the journal (QuickBooks
Desktop).
- Dates: period reports take `start` (default: first entry) and `end`
(default: today, local time); `balance_sheet` and `trial_balance`
take `as_of`.
- Presentation (PDF and XLSX alike, QuickBooks-style): centered company,
title, and period; a column-header row; "$" on subtotals and totals;
negatives in parentheses with decimal points aligned; a rule above every
sum and a double rule under statement totals; a footer with the
accounting basis, preparation date, and journal seq. Multi-member LLCs
get "Members' equity" with members named; the balance sheet separates
retained earnings from the fiscal year's net income (loss).
## Tools — import
@@ -336,10 +355,9 @@ entries.
## Tools — CPA handoff
- `books.export_cpa_package { as_of?, cash_accounts, financing_accounts? }`
→ `{ file_name, content_base64 }`. A zip with `trial-balance.xlsx`,
`general-ledger.xlsx` (every posted line: date, entry, description,
account, class, debit, credit), `profit-and-loss.pdf` (fiscal year to
date, per-account detail), `balance-sheet.pdf` (per-account detail), and
`cash-flow.pdf` (fiscal year to date, indirect method, reconciled to
`cash_accounts`). What the CPA needs; they never
log into the system.
→ `{ file_name, content_base64 }`. A zip with the full set, each
statement as PDF and XLSX: `profit-and-loss`, `balance-sheet`,
`cash-flow` (reconciled to `cash_accounts`), `members-equity`
(multi-member LLCs), `trial-balance`; plus `general-ledger.xlsx` and
`general-journal.xlsx`. Period reports cover the fiscal year to date.
What the CPA needs; they never log into the system.