06/05 Optimize app: Dashboard adds Reconcile button and separate Cashes and Investment
This commit is contained in:
@@ -81,6 +81,20 @@ def index():
|
||||
total_liabilities = get_total_liabilities()
|
||||
net_worth = total_assets - total_liabilities
|
||||
|
||||
total_cash = float(db.session.query(
|
||||
func.coalesce(func.sum(Account.balance), 0)
|
||||
).filter(
|
||||
Account.is_active == True,
|
||||
Account.account_type.in_(['checking', 'savings', 'cash']),
|
||||
).scalar())
|
||||
|
||||
total_investments = float(db.session.query(
|
||||
func.coalesce(func.sum(Account.balance), 0)
|
||||
).filter(
|
||||
Account.is_active == True,
|
||||
Account.account_type.in_(['investment', 'crypto']),
|
||||
).scalar())
|
||||
|
||||
# ── Top expense categories ────────────────────────
|
||||
top_categories = db.session.query(
|
||||
Category.name,
|
||||
@@ -158,6 +172,8 @@ def index():
|
||||
total_assets=total_assets,
|
||||
total_liabilities=total_liabilities,
|
||||
net_worth=net_worth,
|
||||
total_cash=total_cash,
|
||||
total_investments=total_investments,
|
||||
top_categories=top_categories,
|
||||
chart_months=chart_months,
|
||||
chart_income=chart_income,
|
||||
@@ -169,6 +185,71 @@ def index():
|
||||
schwab_warning=schwab_warning)
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/reconcile')
|
||||
@login_required
|
||||
def reconcile_api():
|
||||
"""
|
||||
Return income/expense totals for the period excluding any categories
|
||||
whose name contains 'transfer' (case-insensitive).
|
||||
These are internal account moves that inflate both sides artificially.
|
||||
"""
|
||||
period = request.args.get('period', 'this_month')
|
||||
date_from, date_to, _ = _parse_date_range(period)
|
||||
|
||||
# Find all transfer-like category IDs
|
||||
transfer_cats = Category.query.filter(
|
||||
Category.name.ilike('%transfer%')
|
||||
).all()
|
||||
transfer_ids = [c.id for c in transfer_cats]
|
||||
|
||||
def _sum(txn_type):
|
||||
q = db.session.query(
|
||||
func.coalesce(func.sum(Transaction.amount), 0)
|
||||
).filter(
|
||||
Transaction.transaction_type == txn_type,
|
||||
Transaction.date >= date_from,
|
||||
Transaction.date <= date_to,
|
||||
)
|
||||
if transfer_ids:
|
||||
q = q.filter(
|
||||
db.or_(
|
||||
Transaction.category_id.notin_(transfer_ids),
|
||||
Transaction.category_id.is_(None),
|
||||
)
|
||||
)
|
||||
return float(q.scalar())
|
||||
|
||||
def _sum_transfer(txn_type):
|
||||
if not transfer_ids:
|
||||
return 0.0
|
||||
return float(db.session.query(
|
||||
func.coalesce(func.sum(Transaction.amount), 0)
|
||||
).filter(
|
||||
Transaction.transaction_type == txn_type,
|
||||
Transaction.date >= date_from,
|
||||
Transaction.date <= date_to,
|
||||
Transaction.category_id.in_(transfer_ids),
|
||||
).scalar())
|
||||
|
||||
income = _sum('income')
|
||||
expense = _sum('expense')
|
||||
net = income - expense
|
||||
savings = round(net / income * 100, 1) if income else 0
|
||||
|
||||
excluded_income = _sum_transfer('income')
|
||||
excluded_expense = _sum_transfer('expense')
|
||||
|
||||
return jsonify({
|
||||
'income': income,
|
||||
'expense': expense,
|
||||
'net_cash_flow': net,
|
||||
'savings_rate': savings,
|
||||
'excluded_income': excluded_income,
|
||||
'excluded_expense': excluded_expense,
|
||||
'transfer_categories': [c.name for c in transfer_cats],
|
||||
})
|
||||
|
||||
|
||||
@dashboard_bp.route('/api/fx-history')
|
||||
@login_required
|
||||
def fx_history_api():
|
||||
|
||||
Reference in New Issue
Block a user