{% extends "base.html" %} {% block title %}Dashboard - Fin{% endblock %} {% block content %} {% if show_no_data %}

No accounts selected

Select one or more accounts using the filter above.

{% else %}
{% if current_period %} {{ current_period.period_label }} {% if period_type == 'this_month' %} {% endif %} {% if closed_period %} Closed {% endif %} {% else %} No data {% endif %}
{% if all_accounts and all_accounts|length > 1 %}
{% endif %}
{% if integrity_data and integrity_data.integrity_score < 0.8 %}
Some transactions need classification. The numbers below may be incomplete. Review items →
{% endif %} {% if current_period %}
Cash Flow {{ current_period.period_label }}
{% set max_amount = [current_period.income_cents, current_period.recurring_cents + current_period.discretionary_cents] | max %} {% set income_width = 100 if max_amount > 0 else 0 %} {% set expense_width = ((current_period.recurring_cents + current_period.discretionary_cents) / max_amount * 100) | int if max_amount > 0 else 0 %}
Income
${{ "%.0f"|format(current_period.income_cents / 100) }}
Expenses
${{ "%.0f"|format((current_period.recurring_cents + current_period.discretionary_cents) / 100) }}
{% set net_amount = (current_period.net_cents / 100) | abs %} {% if current_period.net_cents >= 0 %} Kept ${{ "%.0f"|format(net_amount) }} {% else %} Over ${{ "%.0f"|format(net_amount) }} {% endif %} {% if savings_rate_pct %} {{ "%.0f"|format(savings_rate_pct) }}% of income {% endif %}
{% if current_period.income_cents > 0 and current_period.recurring_cents > 0 %}
Baseline: ${{ "%.0f"|format((current_period.income_cents - current_period.recurring_cents) / 100) }}/mo · income minus commitments
{% endif %} {% if avg_cash_flow_cents != 0 %} {% set diff = current_period.net_cents - avg_cash_flow_cents %}
3-month avg?: {{ '+' if avg_cash_flow_cents >= 0 else '-' }}${{ "%.0f"|format((avg_cash_flow_cents / 100) | abs) }}/mo · {% if diff > 0 %} {% if current_period.net_cents >= 0 %} You kept ${{ "%.0f"|format(diff / 100) }} more than usual {% else %} Deficit ${{ "%.0f"|format(diff / 100) }} smaller than usual {% endif %} {% elif diff < 0 %} {% if current_period.net_cents < 0 %} Deficit ${{ "%.0f"|format((-diff) / 100) }} larger than usual {% else %} You kept ${{ "%.0f"|format((-diff) / 100) }} less than usual {% endif %} {% else %} On track with your average {% endif %}
{% endif %} {% if current_period.net_cents < 0 and top_movers %}
Biggest change{{ 's' if top_movers|length > 1 else '' }}: {% for mover in top_movers %} {% if not loop.last %} · {% endif %} {% endfor %}
{% elif current_period.net_cents < 0 and category_breakdown %}
Biggest driver:
{% endif %} {% if avg_income_cents > 0 and current_period.income_cents < (avg_income_cents * 0.9)|int %}
Income ${{ "%.0f"|format((avg_income_cents - current_period.income_cents) / 100) }} lower than usual — spending may be in line
{% endif %} {% if pending_count > 0 %} {% set pending_warning = pending_count > 5 or (current_period.income_cents > 0 and pending_count > (current_period.income_cents / 100 * 0.05)) %}
{% if pending_warning %}⚠{% endif %} {{ pending_count }} pending transaction{{ 's' if pending_count != 1 else '' }} not included
{% endif %}

Cash flow for {{ current_period.period_label }}: Income ${{ "%.0f"|format(current_period.income_cents / 100) }}, Expenses ${{ "%.0f"|format((current_period.recurring_cents + current_period.discretionary_cents) / 100) }}, Net {{ 'kept' if current_period.net_cents >= 0 else 'over' }} ${{ "%.0f"|format((current_period.net_cents / 100) | abs) }}.

Monthly Commitments ${{ "%.0f"|format(current_period.recurring_cents / 100) }} this month
{% set income_amount = current_period.income_cents / 100 %} {% set commit_pct = (current_period.recurring_cents / current_period.income_cents * 100) | int if current_period.income_cents > 0 else 0 %} {% if commit_pct > 50 %}
${{ commit_pct }} of every $100 you earn is committed
{% else %}
That's {{ commit_pct }}% of your income
{% endif %}
{% set bar_color_class = 'red' if commit_pct > 70 else ('yellow' if commit_pct > 50 else '') %}
{% set all_recurring = (subscriptions + bills) | sort(attribute='1', reverse=True) %}
    {% for item in all_recurring[:8] %} {% set display_name = item[8] if item|length > 8 and item[8] else item[0] %} {% set merchant = item[0] %} {% set amount = item[1] %} {% set pc = price_changes_by_merchant.get(merchant) %}
  • {{ display_name }}
    {% if pc %}
    was ${{ "%.2f"|format(pc['old_amount'] / 100) }}
    {% elif item|length > 2 and item[2] %} {% set cadence_labels = {'monthly': 'Monthly', 'quarterly': 'Quarterly', 'yearly': 'Yearly', 'annual': 'Annual', 'biweekly': 'Every 2 weeks', 'weekly': 'Weekly', 'semi-monthly': 'Twice a month', 'recurring': 'Recurring'} %}
    {{ cadence_labels.get(item[2]|lower, item[2]|capitalize) }}
    {% endif %}
    ${{ "%.2f"|format(amount / 100) }}
  • {% endfor %} {% if all_recurring|length > 8 %}
  • View all recurring this month
  • {% endif %}
{% set subs_total = subscriptions | sum(attribute='1') %} {% set bills_total = bills | sum(attribute='1') %} {% if subscriptions and bills %} {% endif %} {% if commitments_nudge is defined and commitments_nudge %} {% endif %}
{% if category_breakdown %}
Spending ${{ "%.0f"|format((current_period.discretionary_cents + current_period.recurring_cents) / 100) }}
{% set top_categories = category_breakdown[:7] %} {% set max_category_amount = top_categories[0][1] if top_categories else 0 %}
    {% for category, net_cents, count, gross_cents, refund_cents in top_categories %} {% set bar_width = (net_cents / max_category_amount * 100) | int if max_category_amount > 0 else 0 %} {% set avg_cents = category_averages.get(category.id, 0) if category_averages else prev_category_map.get(category.id, 0) %} {% set outlier_pct = category_outliers.get(category.id, 0) if category_outliers else (((net_cents - avg_cents) / avg_cents * 100) | int if avg_cents > 0 else 0) %}
  • {{ category.name }}
    ${{ "%.0f"|format(net_cents / 100) }} {% if outlier_pct > 10 %} +{{ outlier_pct }}% {% else %} {% endif %}
    {% if avg_cents > 0 %}
    avg ${{ "%.0f"|format(avg_cents / 100) }}
    {% endif %}
  • {% endfor %}
{% if category_averages or prev_category_map %} {% set total_avg = (category_averages.values() | sum) if category_averages else (prev_category_map.values() | sum) %} {% set total_current = current_period.discretionary_cents + current_period.recurring_cents %} {% set total_diff = total_current - total_avg %} {% endif %}
{% endif %} {% if current_period and current_period.income_cents > 0 %}
Savings Rate [?]
This month {{ "%.0f"|format(savings_rate_pct) }}%
3-mo avg {{ "%.0f"|format(avg_savings_rate_pct) }}%
Target 20%+
{% set bar_pct = savings_rate_pct if savings_rate_pct <= 100 else 100 %} {% set bar_pct = bar_pct if bar_pct >= 0 else 0 %}
{% if savings_tier == "wealth-building" %}Wealth-building {% elif savings_tier == "progress" %}On track {% elif savings_tier == "survival" %}Below target {% else %}Spending more than earning {% endif %}
${{ "%.0f"|format(current_period.net_cents / 100) }} kept of ${{ "%.0f"|format(current_period.income_cents / 100) }} income
{% endif %} {% if pace_data %}
Month Pace day {{ pace_data.days_elapsed }} / {{ pace_data.days_in_month }}
Projected spend ${{ "%.0f"|format(pace_data.projected_spend_cents / 100) }}
Monthly avg ${{ "%.0f"|format(pace_data.avg_monthly_expenses_cents / 100) }}
{% if pace_data.variance_cents > 2000 %} +${{ "%.0f"|format(pace_data.variance_cents / 100) }}  ▲ {{ pace_data.variance_pct }}% {% elif pace_data.variance_cents < -2000 %} −${{ "%.0f"|format((pace_data.variance_cents * -1) / 100) }}  ▼ {{ (pace_data.variance_pct * -1) }}% {% else %} On pace {% endif %} {% if pace_data.top_drivers %}
{% for driver in pace_data.top_drivers %}
{{ driver.category_label }} +${{ "%.0f"|format(driver.variance_cents / 100) }}
{% endfor %}
{% endif %}
{% endif %} {% if attention_items %}
Heads Up
{% for item in attention_items %}
{{ item.title }}
{% if item.detail %}
{{ item.detail }}
{% endif %} {% if item.action_type == 'alert' and not item.actioned %}
{% endif %}
{% endfor %}
{% endif %} {% if periods and periods|length > 1 %}
Your Trend
{% set trend_periods = periods[:6] | reverse | list %} {% set max_kept = trend_periods | map(attribute='net_cents') | map('abs') | max %} {% for p in trend_periods %} {% set is_current = loop.last %} {% set kept_amt = p.net_cents / 100 %} {% set bar_height = ((p.net_cents | abs) / max_kept * 100) | int if max_kept > 0 else 10 %}
{{ '+' if kept_amt >= 0 else '-' }}${{ "%.0f"|format(kept_amt | abs) }}
{{ p.period_label.split()[0][:3] }} {% if is_current and period_type == 'this_month' %} In progress {% endif %}
{% endfor %}
{% set avg_kept = (periods | map(attribute='net_cents') | sum) / (periods | length) %}
{% endif %}
{% else %}
📊

No data for this period

Try syncing your transactions or selecting a different date range.

{% endif %}
{% endif %} {% endblock %}