fix: preserve schema 15 compatibility for v0.1.8

This commit is contained in:
2026-10-17 18:27:53 +09:00
parent a97548c36b
commit 1134f058cc
61 changed files with 2650 additions and 644 deletions
@@ -33,7 +33,7 @@ def _review_cashflow(month: str, db_path: str | None = None, conn=None) -> dict[
start, end = _month_range(month)
rows = conn.execute(f"SELECT {CASHFLOW_KIND_SQL} AS cashflow_kind, t.category, t.subcategory, t.currency, SUM(t.amount) AS amount, COUNT(*) AS count FROM transactions t LEFT JOIN ledger_categories lc ON lc.active=1 AND t.category=lc.category AND t.subcategory=lc.subcategory WHERE t.status='active' AND t.transaction_date>=? AND t.transaction_date<? GROUP BY {CASHFLOW_KIND_SQL}, t.category, t.subcategory, t.currency ORDER BY cashflow_kind, amount DESC", (start, end)).fetchall()
categories = [dict(r) for r in rows]
source_rows = conn.execute(f"SELECT t.id, t.transaction_date, t.type, t.amount, t.currency, t.category, t.subcategory, t.account_id FROM transactions t LEFT JOIN ledger_categories lc ON lc.active=1 AND t.category=lc.category AND t.subcategory=lc.subcategory WHERE t.status='active' AND t.transaction_date>=? AND t.transaction_date<? ORDER BY t.id", (start, end)).fetchall()
source_rows = conn.execute("SELECT t.id, t.transaction_date, t.type, t.amount, t.currency, t.category, t.subcategory, t.account_id FROM transactions t LEFT JOIN ledger_categories lc ON lc.active=1 AND t.category=lc.category AND t.subcategory=lc.subcategory WHERE t.status='active' AND t.transaction_date>=? AND t.transaction_date<? ORDER BY t.id", (start, end)).fetchall()
totals = {}
for item in categories: totals[item['cashflow_kind']] = totals.get(item['cashflow_kind'], 0) + int(item['amount'] or 0)
income, expenses = totals.get('income', 0), totals.get('expense', 0)
@@ -396,7 +396,6 @@ def unlink_account_snapshot(snapshot_id: str, monthly_settlement_id: str, db_pat
return fail("db_error", "account snapshot unlink failed")
def list_monthly_settlement_history(month: str = "", db_path: str | None = None) -> dict[str, Any]:
return fail("unsupported", "run history unavailable")
"""Return immutable close/correction records; lifecycle history is authoritative."""
try:
selected = normalize_month(month) if month else ""
@@ -419,7 +418,6 @@ def list_monthly_settlement_history(month: str = "", db_path: str | None = None)
def get_monthly_settlement_history_record(run_id: str, db_path: str | None = None) -> dict[str, Any]:
return fail("unsupported", "run history unavailable")
"""Read one immutable close/correction record by run ID."""
if not isinstance(run_id, str) or not run_id:
return fail("validation_error", "run_id is required")
@@ -563,7 +561,6 @@ def get_monthly_settlement_summary(month: str = "", db_path: str | None = None)
def get_monthly_settlement_snapshot(month: str = "", run_id: str = "", db_path: str | None = None) -> dict[str, Any]:
return fail("unsupported", "run history unavailable")
"""Read a persisted monthly settlement close without consulting live ledger state."""
if not isinstance(month, str) or not isinstance(run_id, str):
return fail("validation_error", "month and run_id must be strings")
@@ -582,32 +579,38 @@ def get_monthly_settlement_snapshot(month: str = "", run_id: str = "", db_path:
try:
with read_db(db_path) as conn:
tables = {r["name"] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'")}
if "asset_snapshot_runs" not in tables or "account_snapshot" not in tables:
if "asset_snapshot_runs" not in tables or "asset_snapshots" not in tables:
return fail("not_found", "settlement snapshot not found")
run = None
rows = []
legacy = False
metadata: dict[str, Any] = {}
run_cols = {r["name"] for r in conn.execute("PRAGMA table_info(asset_snapshot_runs)")}
if run_id:
# Pre-comprehensive headers are intentionally read without
# assuming schema-16-only columns.
run = conn.execute("SELECT rowid AS _run_rowid,* FROM asset_snapshot_runs WHERE id=?", (run_id,)).fetchone()
if run:
from .assets_service import _snapshot_rows_for_run
rows, metadata = _snapshot_rows_for_run(conn, run_id)
else:
cols = {r["name"] for r in conn.execute("PRAGMA table_info(account_snapshot)")}
cols = {r["name"] for r in conn.execute("PRAGMA table_info(asset_snapshots)")}
if {"snapshot_run_id", "closed_at"} <= cols:
rows = conn.execute("SELECT rowid AS _rowid,* FROM account_snapshot WHERE snapshot_run_id=? AND closed_at<>'' ORDER BY rowid", (run_id,)).fetchall()
rows = conn.execute("SELECT rowid AS _rowid,* FROM asset_snapshots WHERE snapshot_run_id=? AND closed_at<>'' ORDER BY rowid", (run_id,)).fetchall()
if rows:
legacy = True
metadata = {"snapshot_run_id": run_id, "legacy_closed": True, "close_status": "legacy_read_only"}
else:
if selected_month and "settlement_month" in run_cols:
run = conn.execute("SELECT rowid AS _run_rowid,* FROM asset_snapshot_runs WHERE basis_date=? ORDER BY rowid DESC LIMIT 1", (_month_end_date(selected_month),)).fetchone()
elif selected_month:
run = conn.execute("SELECT rowid AS _run_rowid,* FROM asset_snapshot_runs WHERE basis_date=? ORDER BY rowid DESC LIMIT 1", (_month_end_date(selected_month),)).fetchone()
if selected_month:
run_columns = {r["name"] for r in conn.execute("PRAGMA table_info(asset_snapshot_runs)")}
comprehensive_columns = {"snapshot_schema_version", "aggregation_version"}
if comprehensive_columns <= run_columns:
run = conn.execute(
"""SELECT rowid AS _run_rowid,* FROM asset_snapshot_runs
WHERE basis_date=? AND snapshot_schema_version=1
AND aggregation_version='comprehensive-v1'
ORDER BY rowid DESC LIMIT 1""",
(_month_end_date(selected_month),),
).fetchone()
if run is None:
run = conn.execute("SELECT rowid AS _run_rowid,* FROM asset_snapshot_runs WHERE basis_date=? ORDER BY rowid DESC LIMIT 1", (_month_end_date(selected_month),)).fetchone()
if run is None:
basis = _month_end_date(selected_month) if selected_month else ""
if basis:
@@ -650,7 +653,7 @@ def get_monthly_settlement_snapshot(month: str = "", run_id: str = "", db_path:
legacy = not comprehensive
cashflows = []
if comprehensive and "settlement_cashflow_snapshots" in tables:
cashflows = [dict(r) for r in conn.execute("SELECT cashflow_kind,category,subcategory,amount,transaction_count,currency FROM settlement_cashflow_snapshots WHERE monthly_settlement_id=? ORDER BY cashflow_kind,category,subcategory,currency", (rid,))]
cashflows = [dict(r) for r in conn.execute("SELECT cashflow_kind,category,subcategory,amount,transaction_count,currency FROM settlement_cashflow_snapshots WHERE settlement_run_id=? ORDER BY cashflow_kind,category,subcategory,currency", (rid,))]
totals = {"income_total": field("income_total") if comprehensive else None, "expense_total": field("expense_total") if comprehensive else None, "net_cashflow": field("net_cashflow") if comprehensive else None, "total_assets": field("total_assets") if comprehensive else None, "total_liabilities": field("total_liabilities") if comprehensive else None, "net_worth": field("net_worth") if comprehensive else None}
safe_items = []
for row in rows: