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

Financial Dashboard

{% if pending_count > 0 %}
{{ pending_count }} pending transaction{{ 's' if pending_count != 1 else '' }} in this period. Pending charges are included in the calculations shown.
{% endif %} {# Define macro to show account filter badge in section headers #} {% macro account_filter_badge() %} {% if selected_accounts and all_accounts %} {% if selected_accounts|length == 1 %} {% for acc in all_accounts if acc['account_id'] in selected_accounts %} {{ acc['name'] }} {% endfor %} {% else %} {{ selected_accounts|length }} accounts {% endif %} {% elif show_no_data %} No accounts selected {% endif %} {% endmacro %}

Financial Health

{% set min_date = (today - timedelta(days=180)).isoformat() if today else '' %}
{% if start_date and end_date %} Clear {% endif %}
{% if all_accounts %} {% endif %} {% if current_period %}

Period: {{ current_period.period_label }}

{% if expense_only_view %}
💳
Card Spending
${{ "%.2f"|format((current_period.recurring_cents + current_period.discretionary_cents) / 100) }}
{% set total_spending = current_period.recurring_cents + current_period.discretionary_cents %} {% set recurring_pct = (current_period.recurring_cents / total_spending * 100) if total_spending > 0 else 0 %}
{{ "%.0f"|format(recurring_pct) }}% Recurring
{{ "%.0f"|format(100 - recurring_pct) }}% One-time
{{ current_period.transaction_count }} Txns
Recurring
${{ "%.2f"|format(current_period.recurring_cents / 100) }}
{% if current_period.recurring_trend == 'up' %}↑{% elif current_period.recurring_trend == 'down' %}↓{% else %}→{% endif %} vs prev
avg: ${{ "%.0f"|format(current_period.avg_recurring_cents / 100) }}
One-time
${{ "%.2f"|format(current_period.discretionary_cents / 100) }}
{% if current_period.discretionary_trend == 'up' %}↑{% elif current_period.discretionary_trend == 'down' %}↓{% else %}→{% endif %} vs prev
avg: ${{ "%.0f"|format(current_period.avg_discretionary_cents / 100) }}
Total Spending
${{ "%.2f"|format((current_period.recurring_cents + current_period.discretionary_cents) / 100) }}
{{ current_period.transaction_count }} transactions
{% else %} {% set avg_net = current_period.avg_income_cents - current_period.avg_recurring_cents - current_period.avg_discretionary_cents %}
{% if current_period.net_cents >= 0 %} You saved ${{ "%.0f"|format(current_period.net_cents / 100) }} this period {% else %} Spending outpaced income by ${{ "%.0f"|format((-current_period.net_cents) / 100) }} {% endif %}
{% if current_period.incoming_transfer_cents > 0 %}
+ ${{ "%.0f"|format(current_period.incoming_transfer_cents / 100) }} transferred in from other accounts
{% endif %}
{% if avg_net != 0 %} {% set diff = current_period.net_cents - avg_net %} {% set diff_pct = (diff / (avg_net if avg_net > 0 else -avg_net) * 100)|int %} {% if diff > 0 %} {% if current_period.net_cents >= 0 %} ↑ ${{ "%.0f"|format(diff / 100) }} more saved than your 3-month average {% 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 %} ↓ ${{ "%.0f"|format((-diff) / 100) }} less saved than your 3-month average {% endif %} {% else %} → Same as your 3-month average {% endif %} {% else %} {{ current_period.transaction_count }} transactions {% endif %}
{% if current_period.net_cents < 0 and category_breakdown %}
Biggest driver:
{% endif %}
Income
${{ "%.0f"|format(current_period.income_cents / 100) }}
{% if current_period.incoming_transfer_cents > 0 %}
Transfers In
+${{ "%.0f"|format(current_period.incoming_transfer_cents / 100) }}
{% endif %}
Recurring
${{ "%.0f"|format(current_period.recurring_cents / 100) }}
Discretionary
${{ "%.0f"|format(current_period.discretionary_cents / 100) }}
=
Net
${{ "%.0f"|format(current_period.net_cents / 100) }}
{% if current_period.checksum_valid %} {{ current_period.transaction_count }} transactions verified ▸ Show math {% else %} Data mismatch detected ▸ Show details {% endif %}
{% if cross_account_dups %} {% endif %} {% endif %} {% if income_breakdown and not expense_only_view %}

Income Sources (click row to see transactions)

{% for merchant, amount in income_breakdown[:12] %} {% set is_confirmed = merchant in income_sources %} {% set is_excluded = merchant in excluded_sources %}
{{ merchant }} {% if is_confirmed %} ✓ Income {% elif is_excluded %} ✗ Transfer {% else %} ? Unclassified {% endif %}
${{ "%.2f"|format(amount / 100) }}
{% endfor %}
{% if income_breakdown|length > 12 %}

+ {{ income_breakdown|length - 12 }} more sources

{% endif %}
{% endif %} {% else %}

No transaction data available for this period.

Click the Sync button above to load your transactions.

{% endif %}
{% if category_breakdown %} {% set total_category_spending = category_breakdown|sum(attribute='1') %} {% endif %}

Alerts ({{ alerts|length }}{% if total_alerts > alerts|length %} / {{ total_alerts }}{% endif %}) {{ account_filter_badge() }}

{% if show_dismissed %} Including Dismissed {% else %} Show Dismissed {% endif %} Export CSV
{% if alerts %}
{% for item in alerts[:15] %}
{% if item.alert.severity == 'high' %}🔴{% elif item.alert.severity == 'medium' %}🟡{% else %}⚪{% endif %}
{{ item.alert.pattern_type|replace('_', ' ')|title }}: {{ item.alert.merchant_norm }} {% if item.alert.amount_cents %} ${{ "%.2f"|format(item.alert.amount_cents / 100) }} {% endif %}
{{ item.alert.posted_at.strftime('%b %d, %Y') }} — {{ item.alert.detail }}
{% if item.action %}
Marked as: {{ item.action|replace('_', ' ')|title }}
{% endif %}
{% if not item.action %} {% endif %}
{% endfor %}
{% if alerts|length > 15 %}

+ {{ alerts|length - 15 }} more alerts. Export all

{% endif %} {% else %}

No suspicious charges detected. Good news!

{% endif %}
{% if duplicates %}

Possible Duplicate Subscriptions ({{ duplicates|length }}) {{ account_filter_badge() }}

{% for dup in duplicates %}
{% if dup.severity == 'high' %}🔴{% else %}🟡{% endif %}
{{ dup.detail }} ${{ "%.2f"|format(dup.total_monthly_cents / 100) }}/mo
{% for item in dup.items %} {{ item[0] }} (${{ "%.2f"|format(item[1] / 100) }}/mo, {{ item[2] }}){% if not loop.last %}, {% endif %} {% endfor %}
{% endfor %}
{% endif %}

Subscriptions ({{ subscriptions|length }}) & Bills ({{ bills|length }}) {{ account_filter_badge() }}

{% if subscriptions or bills %} {% for sub in subscriptions %} {% set is_known = sub[7] %} {% set display_name = sub[8] %} {% set actual_charge = sub[9] %} {% set cadence = sub[2] %} {% set last_seen = sub[4] %} {% set cadence_suffix = '/yr' if cadence == 'annual' else '/qtr' if cadence == 'quarterly' else '/wk' if cadence == 'weekly' else '/mo' %} {% endfor %} {% for bill in bills %} {% endfor %}
Type Merchant Charge Last Seen Status
🔄 Sub {% if display_name %}{{ display_name }}{% else %}{{ sub[0] }}{% endif %} {% if display_name and display_name != sub[0] %}
{{ sub[0] }}{% endif %}
${{ "%.2f"|format(actual_charge / 100) }}{{ cadence_suffix }} {{ last_seen }} {% if sub[5] %} ⚠️ Dup? {% elif is_known %} ✓ Known {% else %} Active {% endif %}
⚡ Bill {{ bill[0] }} ${{ "%.2f"|format(bill[1] / 100) }}/mo {{ bill[4] }} Active
{% else %}

No subscriptions or bills detected yet.

These are detected after 3+ recurring charges.

{% endif %}
{% if periods|length > 1 %}

Historical Summary {{ account_filter_badge() }}

{% for p in periods %} {% endfor %}
Period Income Recurring Discretionary Net Transactions
{{ p.period_label }} ${{ "%.0f"|format(p.income_cents / 100) }} ${{ "%.0f"|format(p.recurring_cents / 100) }} ${{ "%.0f"|format(p.discretionary_cents / 100) }} ${{ "%.0f"|format(p.net_cents / 100) }} {{ p.transaction_count }}
{% endif %} {% endblock %} {% block scripts %} {% endblock %}