diff --git a/app/routes/reports.py b/app/routes/reports.py index b3e9e80..ee9caa5 100644 --- a/app/routes/reports.py +++ b/app/routes/reports.py @@ -217,3 +217,28 @@ def manual_snapshot(): snap = take_net_worth_snapshot() flash(f'Net worth snapshot saved: {snap.snapshot_date.strftime("%B %d, %Y")}', 'success') return redirect(url_for('reports.index')) + + +@reports_bp.route('/rebuild-snapshot', methods=['POST']) +@login_required +def rebuild_snapshot(): + """Recalculate all balances then replace today's snapshot with fresh data.""" + from app.services.account_service import recalc_all + from app.models.net_worth_snapshot import NetWorthSnapshot + + recalc_all() + + # Delete today's snapshot so take_net_worth_snapshot() creates a fresh one + today = date.today() + existing = NetWorthSnapshot.query.filter_by(snapshot_date=today).first() + if existing: + from app.extensions import db + db.session.delete(existing) + db.session.commit() + + snap = take_net_worth_snapshot() + flash( + f'Balances recalculated and snapshot rebuilt for {snap.snapshot_date.strftime("%B %d, %Y")}.', + 'success' + ) + return redirect(url_for('reports.index')) diff --git a/app/routes/settings.py b/app/routes/settings.py index 08a54d4..01cfeee 100644 --- a/app/routes/settings.py +++ b/app/routes/settings.py @@ -119,11 +119,29 @@ def _category_choices(txn_type='expense'): @settings_bp.route('/') @login_required def index(): + from flask import current_app upcoming = get_upcoming(days=30) rules = RecurringRule.query.filter_by(is_active=True).order_by(RecurringRule.name).all() + smtp_ok = all([ + current_app.config.get('SMTP_HOST'), + current_app.config.get('SMTP_USER'), + current_app.config.get('SMTP_PASSWORD'), + current_app.config.get('ALERT_EMAIL'), + ]) return render_template('settings/index.html', upcoming=upcoming, - rules=rules) + rules=rules, + smtp_ok=smtp_ok, + alert_email=current_app.config.get('ALERT_EMAIL', '')) + + +@settings_bp.route('/recalc-balances', methods=['POST']) +@login_required +def recalc_balances(): + from app.services.account_service import recalc_all + recalc_all() + flash('All account balances recalculated.', 'success') + return redirect(url_for('settings.index')) @settings_bp.route('/profile', methods=['GET', 'POST']) diff --git a/app/routes/teller.py b/app/routes/teller.py index 769cbef..bb1d742 100644 --- a/app/routes/teller.py +++ b/app/routes/teller.py @@ -280,6 +280,23 @@ def sync_all(): return redirect(url_for('teller.sync_preview_view', teller_account_id=mapped[0].id)) +# ── Full resync (reset last_sync_date so next sync fetches full history) ───── + +@teller_bp.route('/resync/', methods=['POST']) +@login_required +def full_resync(teller_account_id): + ta = db.get_or_404(TellerAccount, teller_account_id) + ta.last_sync_date = None + ta.last_teller_txn_id = None + db.session.commit() + flash( + f'{ta.account_name}: reset to full resync. ' + f'Next sync will fetch the last 90 days — duplicates will be skipped automatically.', + 'info' + ) + return redirect(url_for('teller.index')) + + # ── Balance refresh ─────────────────────────────────────────────────────────── @teller_bp.route('/balance/', methods=['POST']) diff --git a/app/templates/reports/index.html b/app/templates/reports/index.html index 4f83d89..ad3eea6 100644 --- a/app/templates/reports/index.html +++ b/app/templates/reports/index.html @@ -140,12 +140,21 @@
Net Worth History -
- - -
+
+
+ + +
+
+ + +
+
{% if nw_history.count > 1 %}
diff --git a/app/templates/settings/index.html b/app/templates/settings/index.html index 7d62630..d75a9c6 100644 --- a/app/templates/settings/index.html +++ b/app/templates/settings/index.html @@ -52,6 +52,53 @@
+ +
+
+
+
Maintenance
+

+ Recalculate every account balance from scratch using all recorded transactions. + Run this after bulk imports or manual adjustments to make sure balances are correct. +

+
+ + +
+
+
+
+
+
Budget Alert Emails
+ {% if smtp_ok %} +
+ + Email alerts enabled → {{ alert_email }} +
+

+ An email is sent automatically when a budget category hits 80% or 100% of its monthly limit. +

+ {% else %} +
+ + Email alerts not configured +
+

+ Add these to your .env file to enable budget alert emails: +

+
SMTP_HOST=smtp.gmail.com
+SMTP_PORT=587
+SMTP_USER=you@gmail.com
+SMTP_PASSWORD=your-app-password
+ALERT_EMAIL=you@gmail.com
+ {% endif %} +
+
+
+ {% if upcoming %}
diff --git a/app/templates/teller/index.html b/app/templates/teller/index.html index 7fd9f16..f05c74b 100644 --- a/app/templates/teller/index.html +++ b/app/templates/teller/index.html @@ -84,6 +84,14 @@ class="btn btn-sm btn-outline-primary" style="font-size:11px;"> Sync +
+ + +
{% else %} Map Account