4.6 KiB
Reconciliation checkpoints design
Purpose
Add a local reconciliation workflow that compares an institution-reported balance with Gizmoduck's ledger for one account on one date. It gives the owner auditable confidence in account balances without changing transactions, creating clearing state, or depending on a particular import format.
Scope
The first version supports every active bank or credit-card account and any chosen as-of date. It records immutable balance checkpoints, shows their current status, and preserves their original calculation for audit.
It does not clear individual transactions, lock a reconciled period, infer a balance from source files, create card statements, or modify financial rows. Those capabilities remain future enhancements.
Domain model
Add ReconciliationCheckpoint to the finance domain:
| Field | Meaning |
|---|---|
account |
Active account being checked; protected from deletion. |
as_of_date |
Inclusive posting-date boundary for the comparison. |
institution_balance |
Balance shown by the bank or card institution, in the account currency. Zero is valid. |
ledger_balance_at_creation |
Sum of account transaction amounts through as_of_date when the checkpoint was recorded. |
difference_at_creation |
institution_balance - ledger_balance_at_creation. |
| timestamps | Immutable creation and update timestamps inherited from the existing base model. |
There is deliberately no uniqueness constraint on (account, as_of_date).
Entering a corrected balance creates another checkpoint rather than rewriting
or deleting history. The latest checkpoint by creation time is the active
checkpoint for that account and date.
The reconciliation service owns creation. It validates the account is active,
accepts decimal-safe balances including zero, calculates the ledger snapshot in
one database transaction, saves the checkpoint, and writes the existing
AuditEvent with the human actor and outcome. No imported source text or
institution credentials are persisted.
Current status and drift
When displaying a checkpoint, Gizmoduck recalculates the account's live ledger
balance through the inclusive as_of_date. It compares that value with the
stored institution balance.
- Reconciled: the live difference is zero.
- Difference: the live difference is nonzero and the original checkpoint also did not reconcile.
- Changed since reconciliation: the original checkpoint reconciled, but later backdated activity changed the live ledger balance.
The checkpoint continues to retain its original ledger balance and difference. This makes late imports and dated manual entries visible without mutating the historical record. Opening balances participate in the ledger sum exactly like other transactions; reports still exclude them from income and spending.
Owner workflow
Add a Reconciliation navigation entry and page.
The page lists active accounts with their most recent checkpoint date and live status. The owner can select an account, choose an as-of date, and enter the institution balance. Before submission, the form shows the current ledger balance and difference for confirmation. Saving records the immutable checkpoint and redirects to the account's reconciliation history.
History shows each checkpoint's as-of date, institution balance, original ledger balance, original difference, current ledger balance, current difference, created time, and status. Amounts never cross currency boundaries. The existing dashboard retains account balances and gains only a compact latest reconciliation status per account.
Error handling and security
Invalid dates and money values receive field-level form errors. Archived or
missing accounts are rejected by the domain service and translated to the
existing safe owner-facing error messages. Forms use the existing CSRF
protection and fixed local HUMAN/local-owner actor. Database exceptions and
financial source data are never exposed in responses.
Verification
Use only synthetic data. Tests cover:
- exact bank and credit-card reconciliation, including a zero institution balance;
- an initial difference and a later corrected checkpoint for the same date;
- a backdated transaction changing a previously reconciled checkpoint;
- opening balances in reconciliation totals;
- archived account rejection and audit behavior;
- migration constraints, owner form validation, CSRF protection, account history, and dashboard status rendering.
Run focused domain and web tests, the full pytest suite, Django migration and system checks, and the existing PostgreSQL permission check.