fix: address account valuation review findings
This commit is contained in:
@@ -4,7 +4,8 @@ from .shared import (
|
||||
Any,
|
||||
CASHFLOW_KIND_SQL,
|
||||
date,
|
||||
db,
|
||||
read_db,
|
||||
read_operation,
|
||||
normalize_date,
|
||||
normalize_month,
|
||||
ok,
|
||||
@@ -16,6 +17,7 @@ from .transactions_write_services import create_ledger_transaction as create_led
|
||||
from .transactions_write_services import delete_ledger_transaction as delete_ledger_transaction
|
||||
from .transactions_write_services import update_ledger_transaction as update_ledger_transaction
|
||||
|
||||
@read_operation
|
||||
def list_ledger_transactions(start_date: str = "", end_date: str = "", type: str = "", category: str = "", person: str = "", account_id: str = "", include_deleted: bool = False, limit: int = 100, db_path: str | None = None) -> dict[str, Any]:
|
||||
clauses=[]; params=[]
|
||||
if not include_deleted:
|
||||
@@ -30,18 +32,19 @@ def list_ledger_transactions(start_date: str = "", end_date: str = "", type: str
|
||||
where = " WHERE " + " AND ".join(clauses) if clauses else ""
|
||||
sql = f"SELECT * FROM transactions{where} ORDER BY transaction_date DESC, created_at DESC LIMIT ?"
|
||||
params.append(max(1, min(int(limit), 1000)))
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
rows = conn.execute(sql, params).fetchall()
|
||||
return ok(transactions=[row_to_dict(r) for r in rows], count=len(rows))
|
||||
|
||||
from .reconciliation_service import get_balance_reconciliation_report as get_balance_reconciliation_report
|
||||
|
||||
@read_operation
|
||||
def list_ledger_budgets(month: str = "", db_path: str | None = None) -> dict[str, Any]:
|
||||
clauses=[]; params=[]
|
||||
if month:
|
||||
clauses.append("month=?"); params.append(normalize_month(month))
|
||||
where = " WHERE " + " AND ".join(clauses) if clauses else ""
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
rows = conn.execute(f"SELECT * FROM budgets{where} ORDER BY month DESC, category", params).fetchall()
|
||||
return ok(budgets=[row_to_dict(r) for r in rows], count=len(rows))
|
||||
|
||||
@@ -52,9 +55,10 @@ def _month_range(month: str) -> tuple[str, str]:
|
||||
end = date(y + (mo == 12), 1 if mo == 12 else mo + 1, 1)
|
||||
return start.isoformat(), end.isoformat()
|
||||
|
||||
@read_operation
|
||||
def get_monthly_cashflow_report(month: str = "", db_path: str | None = None) -> dict[str, Any]:
|
||||
month_norm = normalize_month(month); start, next_month = _month_range(month_norm)
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
type_rows = conn.execute("""SELECT type, SUM(amount) AS total FROM transactions
|
||||
WHERE status='active' AND transaction_date>=? AND transaction_date<?
|
||||
GROUP BY type""", (start, next_month)).fetchall()
|
||||
@@ -72,6 +76,7 @@ def get_monthly_cashflow_report(month: str = "", db_path: str | None = None) ->
|
||||
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)
|
||||
|
||||
@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]:
|
||||
if month:
|
||||
start_date, next_month = _month_range(month); end_op = "<"; end_value = next_month
|
||||
@@ -85,15 +90,16 @@ def get_category_spending_report(month: str = "", start_date: str = "", end_date
|
||||
LEFT JOIN ledger_categories lc ON lc.active=1 AND t.category=lc.category AND t.subcategory=lc.subcategory
|
||||
WHERE t.status='active' AND {CASHFLOW_KIND_SQL}='expense' AND t.transaction_date>=? AND t.transaction_date{end_op}?{person_clause}
|
||||
GROUP BY t.category, t.subcategory ORDER BY total DESC"""
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
rows=conn.execute(sql, params).fetchall()
|
||||
total=sum(int(r["total"] or 0) for r in rows)
|
||||
items=[{"category": r["category"], "subcategory": r["subcategory"], "amount": int(r["total"] or 0), "count": r["count"], "share": round((int(r["total"] or 0)/total),4) if total else 0} for r in rows]
|
||||
return ok(total_expense=total, items=items, count=len(items))
|
||||
|
||||
@read_operation
|
||||
def get_fixed_cost_report(month: str = "", db_path: str | None = None) -> dict[str, Any]:
|
||||
month_norm=normalize_month(month); start,next_month=_month_range(month_norm)
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
rows=conn.execute(f"""SELECT t.category, t.merchant, t.amount, t.transaction_date, t.person, t.memo 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 {CASHFLOW_KIND_SQL}='expense' AND t.is_fixed=1 AND t.transaction_date>=? AND t.transaction_date<?
|
||||
@@ -101,9 +107,10 @@ def get_fixed_cost_report(month: str = "", db_path: str | None = None) -> dict[s
|
||||
items=[row_to_dict(r) for r in rows]
|
||||
return ok(month=month_norm, total_fixed_cost=sum(i["amount"] for i in items), items=items, count=len(items))
|
||||
|
||||
@read_operation
|
||||
def get_budget_vs_actual_report(month: str = "", db_path: str | None = None) -> dict[str, Any]:
|
||||
month_norm=normalize_month(month); start,next_month=_month_range(month_norm)
|
||||
with db(db_path) as conn:
|
||||
with read_db(db_path) as conn:
|
||||
budgets=conn.execute("SELECT * FROM budgets WHERE month=?", (month_norm,)).fetchall()
|
||||
actuals=conn.execute(f"""SELECT t.category, t.subcategory, t.person, SUM(t.amount) AS amount FROM transactions t
|
||||
LEFT JOIN ledger_categories lc ON lc.active=1 AND t.category=lc.category AND t.subcategory=lc.subcategory
|
||||
@@ -116,6 +123,7 @@ def get_budget_vs_actual_report(month: str = "", db_path: str | None = None) ->
|
||||
items.append({"category": b["category"], "subcategory": b["subcategory"], "person": b["person"], "budget": budget, "actual": actual, "remaining": budget-actual, "usage_rate": round(actual/budget,4) if budget else None, "status": "over" if actual>budget else "ok"})
|
||||
return ok(month=month_norm, items=items, total_budget=sum(i["budget"] for i in items), total_actual=sum(i["actual"] for i in items))
|
||||
|
||||
@read_operation
|
||||
def get_investable_cashflow_report(month: str = "", minimum_cash_buffer: int = 0, planned_extra_expense: int = 0, db_path: str | None = None) -> dict[str, Any]:
|
||||
cashflow=get_monthly_cashflow_report(month, db_path)
|
||||
if not cashflow.get("success"):
|
||||
|
||||
Reference in New Issue
Block a user