| |
| """Rebuild D3 balance projections from the immutable journal lines (+ active holds). |
| |
| Verifies the ledger balances BEFORE replacing projections; refuses to rebuild on top of an |
| unbalanced ledger. After rebuild, verifies projections equal the journal-derived balances. |
| Requires --confirm-d3 because it replaces the projection cache (never the journals). |
| |
| Example: |
| python scripts/d3_rebuild_balances.py --db-path /tmp/amanpay-d2/amanpay.db --tenant event \ |
| --confirm-d3 --allow-unknown-fs --json |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import sys |
|
|
| from amanpay.finance.cli import add_common_args, build_runtime_for_cli, emit, resolve_tenant_id |
| from amanpay.finance.verification import (journal_control_totals, rebuild_projections, |
| verify_projections) |
|
|
|
|
| def main(argv=None) -> int: |
| p = argparse.ArgumentParser(description="Rebuild D3 balance projections from journals.") |
| add_common_args(p, tenant_required=False) |
| p.add_argument("--confirm-d3", action="store_true", required=True) |
| args = p.parse_args(argv) |
|
|
| rt = build_runtime_for_cli(args.db_path, allow_unknown_fs=args.allow_unknown_fs) |
| tid = resolve_tenant_id(rt, args.tenant) if args.tenant else None |
|
|
| |
| totals = journal_control_totals(rt.store, tid) |
| if not totals["all_balanced"]: |
| emit({"ok": False, "error": "ledger_unbalanced", "by_currency": totals["by_currency"]}, |
| as_json=args.json) |
| rt.close() |
| return 1 |
|
|
| report = rebuild_projections(rt.store, tid) |
| check = verify_projections(rt.store, tid) |
| emit({"ok": check["ok"], "rebuilt": report["rebuilt"], "mismatches": len(check["mismatches"])}, |
| as_json=args.json) |
| rt.close() |
| return 0 if check["ok"] else 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|