feat: prepaid/deferred amortization schedules and recurring entries

Amortization schedules spread a prepaid expense or deferred revenue over
the months it covers, to the cent, posting on month ends; runs preview
first, are idempotent, honor closed periods, and are exempt from the
receipt policy like depreciation. Recurring entries generate drafts only,
never posts. Six new tools (59 total); four new journal events, replayed
and checked.

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 21:09:43 -07:00
1 parent dbdc8e671f
commit ddb67a2ca2
14 files changed
+1691 -27

No files matched your search

+29
View File
@@ -133,6 +133,35 @@ so the lifetime total is exactly (cost − salvage).
posts the balanced disposal entry. Subject to receipt policy (bill of
sale); monthly depreciation is exempt as an allocation.
## Tools — schedules and recurring entries
Amortization spreads a prepaid expense (asset → expense) or deferred
revenue (liability → revenue) evenly over the months it covers, posting on
each month end, to the cent (the last month absorbs rounding). Like
depreciation, the schedule is the evidence: its postings are exempt from
the receipt policy (the original payment carries the receipt). Recurring
entries never post: they generate drafts, so every real receipt still goes
through the policy.
- `books.create_schedule { kind: "prepaid_expense" | "deferred_revenue",
description, total, start, months, balance_account, pl_account }` →
`{ schedule_id, ..., total, remaining, months: [{ date, amount, posted }] }`.
Prepaid needs an asset and an expense account; deferred revenue a
liability and a revenue account.
- `books.list_schedules {}` → `{ schedules: [...] }`, same shape.
- `books.run_schedules { through?, confirmed? }` → `{ status: "preview" |
"posted", postings: [{ schedule_id, date, description, amount,
entry_id? }] }`: every unposted month ending on or before `through`
(default today). Idempotent; closed periods are refused.
- `books.create_recurring { description, frequency: "weekly" | "monthly" |
"quarterly" | "yearly", start, end?, lines: [{ account, side, amount }] }`
→ `{ recurring_id, ..., generated: [] }`. Lines must balance. Monthly and
longer occurrences are computed from the start date each time, so a
month-end start stays on month ends (Jan 31, Feb 28, Mar 31).
- `books.list_recurring {}` → `{ recurring: [...] }`.
- `books.run_recurring { through? }` → `{ drafts: [{ recurring_id, date,
entry_id }] }`: one draft per occurrence on or before `through`, once.
## Tools — receipts & documents
- `books.attach_receipt { entry_id?, file_name, mime_type, content_base64 }`
+2 -1
View File
@@ -41,7 +41,8 @@ No other statuses.
| Tracking classes (divisional P&L) | Class tracking | done | — |
| CPA handoff package (TB + GL xlsx, P&L + BS pdf, zip) | Reports → export | done | — |
| Budgets | Budgets | todo | R-FUTURE-6 (declined as bloat) |
| Scheduled transactions | Scheduled txns | todo | R-FUTURE-6 |
| Scheduled transactions (recurring entries → drafts) | Scheduled txns / Recurring transactions | partial | hand-verified fixture incl. month-end dates; drafts only, by design |
| Prepaid expense + deferred revenue amortization | Amortization schedules (Xero/QBO add-ons) | partial | hand-verified fixture, to the cent; no golden file yet |
| Automated GnuCash diff harness | — (our `xtask oracle`) | todo | roadmap phase 3 |
Workflow/UI parity (QuickBooks/Xero reference) gets its own catalog when the
+6
View File
@@ -134,6 +134,12 @@ requirement conflict, the requirement wins.
capital. Periods close in order (`CloseOutOfOrder`). The
journal records the target, and replay re-derives the closing entry and
checks every line against the journal.
- **R-? — Prepaid expenses, deferred revenue, and recurring entries.** Was
part of R-FUTURE-6 ("scheduled transactions"). Amortization schedules
post monthly, equal to the cent, idempotently, honoring closed periods;
the journal records each posting with its full entry and replay checks
it. Recurring entries generate drafts only, never posts, so the receipt
policy always applies.
- **R-? — A/R and A/P workflows.** Was R-FUTURE-5.
`ledger-core::arap`: customers, vendors, invoices, bills, payments,
AR/AP aging. MCP tools for the full workflow.