Skip to content

Epic: period-end close (P&L → retained earnings rollover) #8

@avandenberghe

Description

@avandenberghe

Context

Once period locking (#87) is in, the natural follow-on is the period-end close mechanic — the once-a-year (or once-a-season) operation every accounting package runs to:

  1. Sum the net of all P&L accounts (revenue + expense) over the period.
  2. Post a single closing journal entry that zeroes those accounts and credits the result to a retained-earnings equity account.
  3. Lock the period (sets bbaccounts_period_lock_through to the close date).

After close, the P&L accounts start the new period at zero, the retained-earnings account carries the cumulative result, and the locked period becomes immutable.

Why it matters for bbAccounts

Without close, P&L accounts (Revenue 4000, Expenses 5000) accumulate forever — meaningful for a single short period but increasingly noisy as years go by. A guild that runs season-by-season will want to roll over at the season boundary so each season's reports start clean.

Depends on

Design notes

New seeded equity account. The Phase 1 seed has 3010 Opening Balances but no retained-earnings account. The close needs 3020 Retained Earnings (or similar) — added in a Phase 4 migration as additive seed.

Close is one journal entry. For a period ending 2026-12-31:

  • Each P&L account gets a line for its closing balance, in the OPPOSITE direction (revenue accounts get DR for their CR balance; expense accounts get CR for their DR balance).
  • The contra-line is 3020 Retained Earnings, summing to net income (CR if profit, DR if loss).
  • entry_date = the period-end date. reference_type='auto', reference_source='bbaccounts.close', description "Close of period YYYY-MM-DD".

Multi-pool aware. Each currency pool closes independently — one close entry per pool with its own retained-earnings account (per pool, since accounts can't cross pools).

Idempotency. Re-running close for a period that's already closed should be a no-op + clear error message ("period already closed at YYYY-MM-DD"). Track via the existence of a journal entry with reference_source='bbaccounts.close' for that date.

ACP UI

New "Period close" sub-mode under Reports (or a new Periods top-level mode):

  • Pick close date (date input).
  • Preview pane shows the close entry that would be posted (one row per P&L account + the retained-earnings line) for each pool.
  • Confirm button posts the entry through ledger::create_entry() then sets bbaccounts_period_lock_through to the close date in the same transaction.

Acceptance

  • Migration seeds a 3020 Retained Earnings equity account per currency pool.
  • New ACP "Close period" UI with preview + confirm.
  • Close posts one journal entry per pool through the existing create_entry() (so all invariants apply).
  • Same transaction sets the period lock to the close date.
  • Re-running close for an already-closed period rejects cleanly.
  • Service test: full close of a small fixture, verify P&L accounts zero out post-close, retained-earnings equals net income.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions