Skip to content

feat: multi-account financial overview dashboard (#132)#392

Open
sinatragian wants to merge 6 commits intorohitdash08:mainfrom
sinatragian:feat/multi-account-overview
Open

feat: multi-account financial overview dashboard (#132)#392
sinatragian wants to merge 6 commits intorohitdash08:mainfrom
sinatragian:feat/multi-account-overview

Conversation

@sinatragian
Copy link

@sinatragian sinatragian commented Mar 13, 2026

Adds multi-account financial overview dashboard to fix #132.

Backend

  • Full CRUD under /accounts (list, create, get, update, soft-delete)
  • GET /accounts/overview — single GROUP BY query aggregates income/expenses per account; returns per-account balance + {total_assets, total_liabilities, net_worth, unassigned_income, unassigned_expenses, account_count}
  • Account types: BANK, CREDIT, CASH, INVESTMENT, WALLET, OTHER
  • updated_at column with Postgres trigger; soft-delete preserves historical expense links
  • All queries scoped to user_id — no cross-tenant data exposure possible

Frontend

  • /accounts — Accounts page:
    • 4 summary metric cards: Total Assets, Total Liabilities, Net Worth (green/red), Account Count
    • Accounts list with type badge, currency, color swatch, calculated balance
    • "New Account" dialog: name, type (select), currency, initial_balance, color
    • Inline edit (PATCH) and soft-delete per account
    • Loading skeletons, empty state, toast notifications
  • Wired into React Router and navbar
  • Files: app/src/api/accounts.ts (full API layer), app/src/pages/Accounts.tsx, route in App.tsx, nav link in Navbar.tsx

Security note

Every query is scoped to the authenticated user's user_id — list, get, overview GROUP BY, and unassigned totals. A valid JWT for user A cannot read or infer user B's account balances even with a guessed account_id.

Closes #132

- Account model (BANK/CREDIT/CASH/INVESTMENT/WALLET/OTHER)
- Full CRUD: GET/POST/PATCH/DELETE /accounts
- GET /accounts/overview — aggregate balance, income, expenses per account
- Net worth = total_assets - total_liabilities (CREDIT accounts = liabilities)
- Soft delete (deactivate) to preserve expense history
- Expense.account_id FK (nullable, backward-compatible)
- Migration 005_accounts.sql
- 27 tests covering CRUD, overview, balances, auth

Closes rohitdash08#132
…ted_at

Fix 1 — N+1 → single GROUP BY query in GET /accounts/overview
  Previously emitted 2 separate SUM queries per account (O(2n) round-trips).
  Now a single query groups income/expense amounts by account_id using
  conditional aggregation (CASE WHEN), then joins back to the account list
  in Python. The two unassigned-expense scalars are collapsed into one query
  as well.

Fix 2 — GET/PATCH /accounts/<id> returns 404 for deactivated accounts
  _get_or_404 now checks active=True alongside user ownership. Soft-deleted
  accounts are inaccessible via the API; historical expense links are
  preserved in the DB. Explicit comment documents the design decision.

Fix 3 — Add updated_at to Account model and migration
  Added updated_at column (default NOW(), auto-updated via SQLAlchemy
  onupdate + Postgres trigger in migration). Exposed in _account_to_dict.
  Migration is idempotent (ALTER TABLE ... ADD COLUMN IF NOT EXISTS).

Tests added:
  - test_overview_no_n_plus_1_with_multiple_accounts (end-to-end GROUP BY)
  - test_get_deactivated_account_returns_404
  - test_patch_deactivated_account_returns_404
  - test_account_has_updated_at_field
  - test_updated_at_changes_on_patch
@sinatragian
Copy link
Author

Security note — user_id isolation in all financial queries

Every query in this implementation is scoped to the authenticated user's user_id:

  • GET /accountsfilter_by(user_id=uid, active=True)
  • GET /accounts/<id>_get_or_404 checks both acc.user_id != user_id and acc.active
  • GET /accounts/overview → the GROUP BY aggregation filters Expense.user_id == uid before joining; no row from another user can appear in the result
  • Unassigned-expense totals also filter by Expense.user_id == uid

This means a valid JWT for user A cannot be used to read, modify, or infer the balance of user B's accounts — even if the account_id is guessed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-account financial overview dashboard

1 participant