feat: deliver schema 15 monthly settlement lifecycle
This commit is contained in:
@@ -68,13 +68,65 @@ def get_monthly_cashflow_report(month: str = "", db_path: str | None = None) ->
|
||||
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}""", (start, next_month)).fetchall()
|
||||
category_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, next_month)).fetchall()
|
||||
type_totals = {r["type"]: int(r["total"] or 0) for r in type_rows}
|
||||
cashflow_totals = {r["cashflow_kind"]: int(r["total"] or 0) for r in cashflow_rows}
|
||||
income = cashflow_totals.get("income", 0)
|
||||
expenses = cashflow_totals.get("expense", 0)
|
||||
net_cashflow = income - expenses
|
||||
savings_rate = round(net_cashflow / income, 4) if income else None
|
||||
return ok(month=month_norm, income=income, expenses=expenses, net_cashflow=net_cashflow, savings_rate=savings_rate, by_type=type_totals, by_cashflow_kind=cashflow_totals)
|
||||
categories = [row_to_dict(r) for r in category_rows]
|
||||
unknown_total = {}
|
||||
unknown_category_items = []
|
||||
for row in categories:
|
||||
if row.get("cashflow_kind") == "unknown":
|
||||
currency = row.get("currency") or "미지정"
|
||||
unknown_total[currency] = unknown_total.get(currency, 0) + int(row.get("amount") or 0)
|
||||
unknown_category_items.append(row)
|
||||
return ok(month=month_norm, income=income, expenses=expenses, net_cashflow=net_cashflow, savings_rate=savings_rate, by_type=type_totals, by_cashflow_kind=cashflow_totals, categories=categories, unknown_total=unknown_total, unknown_category_items=unknown_category_items)
|
||||
|
||||
@read_operation
|
||||
def get_settlement_cashflow_snapshot(monthly_settlement_id: str, db_path: str | None = None) -> dict[str, Any]:
|
||||
"""Read only the persisted cashflow rows for a closed settlement."""
|
||||
with read_db(db_path) as conn:
|
||||
rows = conn.execute("""SELECT cashflow_kind, category, subcategory, amount,
|
||||
transaction_count AS count, currency
|
||||
FROM settlement_cashflow_snapshots
|
||||
WHERE monthly_settlement_id=?
|
||||
ORDER BY cashflow_kind, category, subcategory, currency""", (monthly_settlement_id,)).fetchall()
|
||||
settlement = conn.execute("SELECT status FROM monthly_settlement WHERE id=?", (monthly_settlement_id,)).fetchone()
|
||||
categories = [row_to_dict(row) for row in rows]
|
||||
if not categories:
|
||||
saved = bool(settlement and settlement["status"] == "closed")
|
||||
return ok(saved=saved, categories=[], income=0 if saved else None, expenses=0 if saved else None,
|
||||
net_cashflow=0 if saved else None, month="", message="저장된 현금흐름이 비어 있습니다." if saved else "저장된 현금흐름 스냅샷이 없습니다.")
|
||||
by_currency = {}
|
||||
for row in categories:
|
||||
cur = row.get("currency") or "미지정"
|
||||
bucket = by_currency.setdefault(cur, {"income": 0, "expenses": 0, "net_cashflow": 0})
|
||||
if row["cashflow_kind"] == "income": bucket["income"] += int(row["amount"] or 0)
|
||||
elif row["cashflow_kind"] == "expense": bucket["expenses"] += int(row["amount"] or 0)
|
||||
for bucket in by_currency.values(): bucket["net_cashflow"] = bucket["income"] - bucket["expenses"]
|
||||
only = next(iter(by_currency.values())) if len(by_currency) == 1 else None
|
||||
unknown_total = {}
|
||||
unknown_category_items = []
|
||||
for row in categories:
|
||||
if row.get("cashflow_kind") == "unknown":
|
||||
currency = row.get("currency") or "미지정"
|
||||
unknown_total[currency] = unknown_total.get(currency, 0) + int(row.get("amount") or 0)
|
||||
unknown_category_items.append(row)
|
||||
return ok(saved=True, categories=categories, unknown_total=unknown_total, unknown_category_items=unknown_category_items, by_currency=by_currency,
|
||||
income=only["income"] if only else None,
|
||||
expenses=only["expenses"] if only else None,
|
||||
net_cashflow=only["net_cashflow"] if only else None, month="")
|
||||
|
||||
@read_operation
|
||||
def get_category_spending_report(month: str = "", start_date: str = "", end_date: str = "", person: str = "", db_path: str | None = None) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user