Browse Source

docs: explain reconciliation workflow

master
Codex 2 months ago
parent
commit
5da04d1968
  1. 9
      README.md
  2. 18
      docs/local-workflows.md
  3. 60
      tests/test_mvp_acceptance.py

9
README.md

@ -31,6 +31,15 @@ Start with [contributor instructions](AGENTS.md), then read the
Owners can follow the copy-paste [local workflow guide](docs/local-workflows.md)
from reviewed import through reports, export, and verified backup restoration.
## Reconciliation
Open **Reconciliation**, select an account, and enter a checkpoint date with the
institution balance. Review the preview before confirming the checkpoint.
Checkpoint dates are inclusive, so they include activity posted on that date. A
new checkpoint corrects a prior checkpoint. Newly imported activity or a later
backdated transaction can change a previously reconciled status, so review the
status and record a new checkpoint after verifying the institution balance.
## Architecture
The approved technical choices are:

18
docs/local-workflows.md

@ -25,8 +25,8 @@ local database tools such as DBeaver. The database name is `gizmoduck`. Use
routine inspection. The migration and bootstrap roles have broader privileges
and are for maintenance only.
The navigation provides Accounts, Transactions, Categories, Payees, Reports,
and Exports. Create accounts, categories, and payees before assigning them.
The navigation provides Accounts, Transactions, Categories, Payees, Reconciliation,
Reports, and Exports. Create accounts, categories, and payees before assigning them.
References may be archived, but historical transactions continue to show them.
Transaction editing is limited to description, payee, and category. Amount,
dates, account, kind, import provenance, transfers, refunds, statements, and
@ -104,6 +104,20 @@ excluded from cash flow. A card purchase is counted once as an expense. Spending
is grouped by expense category with a visible Uncategorized row, and account
balances remain separate by account and currency.
## Reconcile an account balance
Open **Reconciliation** from the navigation. Select the account, enter the
checkpoint date and the institution balance, then choose **Preview** to review
the ledger balance and difference. Choose **Confirm checkpoint** only after the
preview is correct. Checkpoint dates are inclusive, so the ledger includes every
transaction posted on that date.
A checkpoint is immutable. To correct an earlier checkpoint, record a new one
for the appropriate account and date. Newly imported activity or a transaction
added later with a backdated posting date can change a previously reconciled
status; review the reconciliation status and record a new checkpoint when the
institution balance has been verified.
## Export portable data
Use `http://127.0.0.1:8000/exports/` to download a ZIP, or create one under the

60
tests/test_mvp_acceptance.py

@ -2,6 +2,7 @@
import json
from contextlib import contextmanager
from datetime import date
from decimal import Decimal
from pathlib import Path
from types import SimpleNamespace
@ -18,8 +19,8 @@ from core.backup import (
verify_backup,
)
from finance.exports import build_export
from finance.models import SourceRecord, Transaction
from finance.services import Actor, create_account, create_category
from finance.models import ReconciliationCheckpoint, SourceRecord, Transaction
from finance.services import Actor, create_account, create_category, record_transaction
from ingestion import services as import_services
@ -28,6 +29,61 @@ ACTOR = Actor(kind="AGENT", identifier="synthetic-mvp-acceptance")
FIXTURE = Path("tests/fixtures/synthetic/ingestion/bank.csv")
def test_owner_reconciliation_workflow_reports_backdated_activity():
account = create_account(
name="Synthetic acceptance reconciliation account",
kind="BANK",
currency="BRL",
actor=ACTOR,
)
record_transaction(
account_id=account.pk,
amount=Decimal("-50.00"),
posting_date=date(2026, 4, 10),
kind="EXPENSE",
actor=ACTOR,
)
browser = Client()
checkpoint = {
"account": account.pk,
"as_of_date": "2026-04-30",
"institution_balance": "-50.00",
}
preview = browser.post(
reverse("reconciliation-index"),
{"action": "preview", **checkpoint},
)
assert preview.status_code == 200
assert "Preview" in preview.content.decode()
assert "0.00 BRL" in preview.content.decode()
assert ReconciliationCheckpoint.objects.count() == 0
confirmed = browser.post(
reverse("reconciliation-index"),
{"action": "confirm", **checkpoint},
)
assert confirmed.status_code == 302
assert confirmed.url == reverse("reconciliation-history", args=[account.pk])
assert ReconciliationCheckpoint.objects.count() == 1
record_transaction(
account_id=account.pk,
amount=Decimal("-5.00"),
posting_date=date(2026, 4, 15),
kind="EXPENSE",
actor=ACTOR,
)
dashboard = browser.get(
reverse("home"),
{"month": "2026-04", "currency": "BRL"},
)
assert dashboard.status_code == 200
assert "Synthetic acceptance reconciliation account" in dashboard.content.decode()
assert "Changed since reconciliation" in dashboard.content.decode()
def test_synthetic_import_to_report_export_and_backup_workflow(
tmp_path, settings, monkeypatch
):

Loading…
Cancel
Save