feat: period-scoped P&L and as-of balance sheet; closes don't zero periods
The engine only had life-of-ledger P&L and balance sheet totals, so every export labeled with a date range or as-of date (PDF, XLSX, CPA package) printed all-time numbers under that label. Core now has: - profit_and_loss_for_period(start, end, class?) and balance_sheet_as_of(as_of), each with a line per account (StatementLine: code, name, normal-side amount) and exact totals. - Ledger::closing_entry_ids(). Period statements exclude closing entries. A close (Dr revenue / Cr expense / net to Retained Earnings, dated at period end) moves earnings; it doesn't earn or spend. Counting it made a closed period's P&L read as break-even, and made the cash flow show zero net income plus a fake "owner contribution" to Retained Earnings. The as-of balance sheet keeps closing entries: after a close the earnings belong in Retained Earnings, not current earnings. Tests: tests/statements.rs, a two-month fixture with every expected number computed by hand (Jan NI 1,700; Feb 880; balance sheets on four dates; a closed January still reporting 1,700 and a cash flow with one financing line). Mutation-checked: dropping the closing exclusion fails it (0 vs 1,700). Properties: period P&Ls partition a year at any split, and a range's net income equals the change in current earnings across it, with A = L + E on both dates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtYEaR6M6Ybdhm6X98BWNu
This commit is contained in:
1 parent
5548902f27
commit
c6faeeb827
7 files changed
+505
-13
No files matched your search
@@ -568,6 +568,16 @@ impl Ledger {
|
||||
self.periods.get(&id)
|
||||
}
|
||||
|
||||
/// Ids of every period-closing entry. Period statements (P&L, cash
|
||||
/// flow) exclude them: a close moves earnings into retained earnings; it
|
||||
/// earns and spends nothing, so counting it would zero out the period.
|
||||
pub fn closing_entry_ids(&self) -> std::collections::BTreeSet<EntryId> {
|
||||
self.periods
|
||||
.values()
|
||||
.filter_map(|p| p.closing_entry)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// All periods, in creation order.
|
||||
pub fn list_periods(&self) -> Vec<&Period> {
|
||||
let mut ids: Vec<PeriodId> = self.periods.keys().copied().collect();
|
||||
|
||||
Reference in new issue
Block a user