1 changed files with 99 additions and 0 deletions
@ -0,0 +1,99 @@ |
|||||||
|
# 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. |
||||||
Loading…
Reference in new issue