Files
Personal-Finance-Management/README.md
T
2026-06-06 11:49:51 -04:00

26 KiB
Raw Blame History

PFM — Personal Finance Manager

A self-hosted personal finance web application. Track income, expenses, investments, budgets, and savings goals. AI-powered financial assistant and receipt OCR built in. Sync bank accounts via Teller, Plaid, or Schwab.

Live at: https://pfm.ngodanguyen.tech


Table of Contents

  1. Features Overview
  2. Getting Started
  3. Dashboard
  4. Transactions
  5. Accounts
  6. Categories
  7. Budgets
  8. Goals & Savings
  9. Investments
  10. AI Assistant
  11. Reports & Export
  12. Settings
  13. Security & 2FA
  14. Recurring Transactions
  15. Bank Statement Import
  16. Receipt OCR
  17. Teller Bank Sync
  18. Schwab Bank Sync
  19. Plaid Bank Sync
  20. USD → VND Rate Widget
  21. System Logs
  22. Keyboard Shortcuts & Tips

Features Overview

Module What it does
Dashboard Net worth, savings rate, cash flow chart, budget gauges, upcoming bills, reconcile, AI insight, USD/VND rate
Transactions Income/expense entry, transfer, inline category, bulk actions, quick date filters, duplicate detection, split transaction, filtered export
Accounts Multiple account types, auto-calculated or provider-synced balances (Teller / Plaid / Schwab)
Categories Custom expense + income categories with color and icon
Budgets Monthly spending limits with progress bars, budget vs actual chart, rollover support
Goals Savings goals with contribution tracking, projected completion, emergency fund tracker
Investments Stock/ETF/crypto portfolio, per-account view, price alerts, price history chart, live price fetch
AI Assistant Chat with your finances via Groq (free), daily auto-insight, streaming responses
Receipt OCR Drag-drop a receipt image — AI extracts and fills the form
Reports Monthly/quarterly/yearly summaries, net worth history, category spending trends, PDF/CSV/Excel export
Teller Live US bank sync (mTLS), balance refresh, transaction import, webhook
Schwab Schwab brokerage sync — balance, stock/ETF positions, transaction import, daily auto-sync
Plaid 12,000+ US banks/credit unions via Plaid — cursor sync, credit card billing, webhook auto-import
Bank Import Upload PDF/OFX/QFX/CSV bank statements — auto-categorizes, preview before import
Security TOTP 2FA, session idle timeout, rate limiting, audit log, at-rest encryption for bank tokens
System Logs DB-backed application log viewer with colour-coded levels, search, module filter, and purge

Getting Started

First Login

  1. Navigate to https://pfm.ngodanguyen.tech
  2. Enter the username and password created during setup (python scripts/init_db.py)
  3. Check "Remember me" to stay logged in across browser sessions
  1. Add accounts — add your bank accounts, cash wallet, and credit cards first
  2. Review categories — 21 default categories are seeded; add custom ones if needed
  3. Set your currency — go to Settings → Profile and pick from 8 currency options
  4. Connect bank sync — connect Teller (US banks), Plaid (12,000+ institutions), or Schwab for automatic data
  5. Add transactions — enter income/expenses manually, import a bank statement, or sync from a provider
  6. Set budgets — once you have spending data, set monthly limits per category
  7. Create goals — add savings goals and start contributing
  8. Add investments — manually or via Schwab sync
  9. Enable 2FA — recommended: Settings → Security → Two-Factor Authentication

Dashboard

The dashboard is the home screen — accessible from the sidebar or by clicking the PFM logo.

Period Selector

Three buttons in the top-right: This Month, Last Month, Custom (date range picker). All summary cards update for the chosen period.

Summary Cards

  • Income — total income for the selected period
  • Expenses — total expenses for the selected period
  • Net Cash Flow — income minus expenses
  • Savings Rate — net cash flow ÷ income (green ≥ 20%, blue > 0%, red negative)
  • Net Worth — total assets minus liabilities
  • Checking & Savings — sum of checking, savings, and cash account balances
  • Investments — sum of investment and crypto account balances

Reconcile Button

Click Reconcile (next to period selector) to recalculate income, expenses, net cash flow, and savings rate while excluding internal transfers. This shows your true external cash flow. A notice shows the excluded transfer amounts and which categories were excluded. Click again to toggle back to the original totals.

Cash Flow Chart

Bar chart showing the last 6 months of income (green) vs expenses (red).

Upcoming Bills

Table of recurring rules due within the next 14 days — category icon, rule name, due date (color-coded: today/overdue in red, within 3 days in amber), amount, and account. Links to the Recurring page.

Accounts Panel

Lists all active accounts with current balances. Credit cards show Amount Owed (positive) instead of a raw negative balance.

AI Daily Insight

Auto-generated summary of your finances at midnight. Click Generate Now for an on-demand insight. Click Open AI → for the full chat interface.

USD → VND Widget

Reference-only exchange rate. Click ↻ to refresh without reloading. Click the widget to expand a 30-day trend chart. A ⚠ stale indicator appears if the rate is older than one day.

Schwab Expiry Warning

A banner appears on the dashboard when your Schwab refresh token expires within 2 days. Click the link to reconnect.


Transactions

Viewing Transactions

Navigate via Transactions in the sidebar. Two tabs: Expenses and Income (with total counts).

Filtering

  • Search — matches description and notes fields
  • Category dropdown
  • Account dropdown
  • Date range — from/to pickers
  • Amount range — min/max amount
  • Quick date filters — "This Month" and "Last Month" buttons above the filter bar; active button is highlighted; ✕ clears the quick filter
  • Saved filter presets — save the current filter combination under a name; reload it from the dropdown in one click

Plaid Review Banner

When transactions are imported via Plaid webhook without a category, a purple banner at the top of the Transactions page shows the count and a link to review and categorize them. The sidebar Transactions link also shows a badge with the count.

Adding a Transaction

Use the topbar Income / Expense buttons, or use the sidebar links. The form includes:

  • Type toggle (Income / Expense) — switches available categories
  • Description, amount, date, account, category, notes
  • Duplicate detection — if the same amount on the same date already exists, a yellow warning banner appears before you save

Inline Category Change

Click the category dropdown on any transaction row in the table to change the category. The change saves via AJAX — no page reload.

Editing and Deleting

Click Edit to open the full form. Click Del to delete permanently.

Split Transaction

Click the ✂ (scissors) icon on any transaction row to split it into multiple parts:

  • A split page shows the original transaction details and two default rows
  • Assign a different category, description, and amount to each part
  • A live Remaining counter shows how much is left to allocate
  • Add or remove rows as needed; the total must equal the original amount
  • On confirm, the original transaction is replaced by the individual split transactions

Bulk Actions

Check one or more transaction rows (or use the select all header checkbox) to reveal the bulk action toolbar:

  • Set category — apply a category to all selected rows at once; dropdowns update in the table without reload
  • Delete — delete all selected transactions after a confirmation dialog

Export Filtered View

In the filter bar, CSV and Excel buttons export the current filtered view (respects all active filters — search, category, account, date range, amount range). Exports are streamed for memory efficiency — no row limit.

Transfers

Click Transfer in the topbar. Creates a single transfer record between two accounts. Transfers are excluded from income/expense totals.


Accounts

Account Types

checking / savings / cash / credit_card / crypto / investment / other

Balance Sources

  • Manually managed — balance is computed from transactions (income expenses ± transfers). Updates after every transaction.
  • Teller-linked — live balance from Teller API. Blue Teller badge. Updated after sync or Refresh click.
  • Schwab-linked — balance from Schwab snapshot (liquidation value). Green Schwab badge. Updated after Balance & Positions click or daily auto-sync.
  • Plaid-linked — live balance from Plaid API. Purple Plaid badge. Updated after sync or Refresh click.

Teller Account Actions

Button What it does
Refresh Pulls live balance from Teller API (AJAX)
Sync Opens transaction preview — import new transactions
Reset Clears sync cursor — next Sync re-fetches full 90-day history

Schwab Account Actions

Button What it does
Balance & Positions Fetches live balance + investment holdings from Schwab
Transactions Opens transaction import preview

Plaid Account Actions

Button What it does
Refresh Pulls live balance from Plaid API (AJAX)
Sync Opens transaction preview — import new transactions
Billing Fetches credit card due date, minimum payment, and statement balance

Plaid Credit Card Billing Card

For Plaid-linked credit cards, a billing card on the account shows: due date, days remaining, minimum payment, and last statement balance. Updated by clicking Billing.

Credit Cards

Credit cards show two values:

  • Amount Owed — how much you currently owe (positive number)
  • This Month — expenses charged to the card this calendar month

Opening Balance

When creating a new account, enter the current balance in the Opening Balance field. For credit cards, enter a negative number to indicate existing debt (e.g. -500 if you owe $500).


Categories

Navigate via the sidebar footer → Categories.

Default Expense (14): Housing, Food & Dining, Transport, Utilities, Health, Entertainment, Shopping, Education, Insurance, Personal Care, Travel, Subscriptions, Gifts, Other

Default Income (7): Salary, Freelance, Business, Investment, Rental, Gift Received, Other Income

System categories (grey badge) cannot be deleted — color and icon can still be changed. Custom categories can be fully edited and deleted.


Budgets

Navigate via Budgets in the sidebar. Use ◀ ▶ to navigate months.

Setting Budgets

Click Add Budget in the topbar to set a monthly limit for a category. Click Edit on an existing row to adjust.

Budget vs Actual Chart

A horizontal bar chart at the top of the page shows Spent (color-coded green/amber/red) vs Budget limit per category — for all categories that have a budget set this month.

Progress Bars

Each row shows a progress bar: green (< 80%) → amber (8099%) → red (≥ 100%). Rows that exceed the limit have a red background tint.

Rollover

Enable rollover on a budget to carry unused amounts forward to the next month. A blue +rollover badge shows the carried-over amount on the row.

Copy from Previous Month

Click Copy from [month] at the top right to duplicate last month's budgets to the current month in one click. Also available on the empty-state screen.


Goals & Savings

Navigate via Goals in the sidebar.

  • Create goals with target amount, target date, color, and icon
  • Add contributions with dates and notes; view contribution history
  • Progress bar + projected completion date (based on average monthly contribution)
  • Auto-completes when 100% is reached
  • Emergency Fund Tracker — shows how many months your liquid assets (checking + savings + cash accounts) cover at your current monthly spending rate, with 3-month and 6-month targets

Investments

Navigate via Investments in the sidebar.

Portfolio Overview

Summary cards: Total Value, Total Cost, Unrealized P&L, Return %. Allocation doughnut chart by asset type. Sidebar "By Account" breakdown.

Price Alerts

A yellow banner at the top of the Investments page appears when any holding moves ≥ 5% intraday. The banner lists each mover with its ticker and change percentage. Dismiss with ×. Alerts are recalculated daily when prices update.

Investment Detail Page

Click any holding to see the detail page:

  • Current price, day change, P&L
  • Price history chart — 1W / 1M / 3M / 6M / 1Y timeframes; fetched from Yahoo Finance
  • Full buy/sell/dividend/split transaction log

Per-Account View

If investments span multiple accounts (e.g. Schwab Individual + Schwab Roth IRA), holdings are grouped into separate sections — one per account. Each section shows the account name, total value, and its own holdings table. Same ticker in different accounts stays as separate rows.

Syncing from Schwab

Click Sync Schwab in the topbar to pull the latest holdings and balances for all mapped Schwab accounts at once.

Adding a Holding Manually

Click Add Holding in the topbar. After saving, go to the detail page to record buy transactions.

Ticker Format

  • US Stocks/ETFs: AAPL, VOO, QQQ
  • Crypto: BTC-USD, ETH-USD
  • International: Yahoo Finance suffix (e.g. VIC.VN)

Refreshing Prices

Click Refresh Prices in the topbar. Prices also auto-update daily at 4 PM weekdays via cron.


AI Assistant

Navigate via AI Assistant in the sidebar.

The AI has access to the last 90 days of transactions, budget status, goals, investments, and net worth. No personal names or identifiers are sent — only aggregated financial figures.

  • Type a message and press Enter to send; Shift+Enter for a new line
  • Responses stream word-by-word (SSE)
  • Eight quick-question suggestion buttons
  • Daily Insight — auto-generated at midnight; click Generate Now to create on demand
  • Chat History — all past responses with timestamps

AI Model

Change in Settings → Profile → AI Model:

  • llama-3.3-70b-versatile — best quality (default)
  • llama-3.1-8b-instant — faster, slightly lower quality

Reports & Export

Navigate via Reports in the sidebar.

Four tabs: Monthly, Quarterly, Yearly, Tax Year.

Export buttons in the topbar:

  • CSV — plain text, streamed (no row limit)
  • Excel — color-coded (green = income, red = expense), formatted, streamed
  • PDF — printable report via WeasyPrint

Net Worth History — line chart from monthly snapshots. Snapshot Now saves today's values immediately.

Category Spending Trends — top 6 categories over the last 6 months.


Settings

Navigate via the gear icon at the bottom of the sidebar.

Section What you can change
Profile Display name, email, timezone, currency (8 options), AI model
Password Change password (requires current password)
Security Enable/disable TOTP 2FA; view Audit Log
Recurring Create/edit/pause recurring transaction rules
Import Upload CSV or bank statement for bulk import
System Logs View, search, and purge application logs

All Settings sub-pages have a ← Settings back button in the topbar.


Security & 2FA

Two-Factor Authentication (TOTP)

  1. Go to Settings → Security section
  2. Click Enable Two-Factor Authentication
  3. Scan the QR code with an authenticator app (Google Authenticator, Authy, etc.) or enter the manual key
  4. Enter the 6-digit code to confirm setup
  5. On future logins, you will be prompted for a 6-digit code after password entry

To disable 2FA: Settings → Security → Disable (requires password confirmation).

The TOTP verify endpoint is rate-limited (10/min, 30/hr). After 5 failed attempts in one session, the login is invalidated and you must start over.

Session Idle Timeout

Sessions expire after 60 minutes of inactivity (configurable via SESSION_IDLE_MINUTES in .env).

Audit Log

Settings → Audit Log shows a paginated, filterable log of security events with IP addresses and timestamps:

  • Login success / failure (with and without 2FA)
  • 2FA enabled / disabled
  • Password changed
  • Schwab connected / disconnected

Filter by event type. Purge dropdown removes entries older than 7, 30, or 90 days.

At-Rest Encryption

Teller, Schwab, and Plaid OAuth tokens are encrypted in the database using AES-128 (Fernet). Changing SECRET_KEY in .env invalidates stored tokens — all providers would need to reconnect.


Recurring Transactions

Navigate via Settings → Recurring.

Rules auto-create transactions on a schedule. Frequencies: daily, weekly, biweekly, monthly, quarterly, yearly.

  • Set start date, optional end date, account, category, and amount
  • Pause/enable individual rules without deleting them
  • Run Now button processes all overdue rules immediately
  • Upcoming (30 days) panel shows next-due dates for all active rules
  • Rules also appear on the Dashboard upcoming bills widget when due within 14 days

Bank Statement Import

Navigate via Settings → Import Statement (sidebar) or Settings → Import.

Supported Formats

Format Notes
PDF Digital (text-based) PDFs only — scanned/image PDFs are rejected. Uses table extraction first; falls back to AI parsing via Groq if no structured table is found
OFX / QFX Standard Open Financial Exchange format (most US banks)
CSV Chase, BofA, Citi, Capital One, Discover, Amex, USAA, Wells Fargo, and Generic CSV
Custom CSV Map your own column headers when the bank format isn't recognized

Import Flow

  1. Upload the file
  2. Preview table — per-row checkboxes, editable category dropdowns, bulk category apply, bulk type toggle
  3. Click Confirm Import to write to the database

Duplicate Detection

  • OFX/QFX: uses the FITID field
  • CSV/PDF: matches on date + amount + description + account

Auto-Categorization

200+ keyword rules automatically assign categories to imported transactions. Override any row in the preview before confirming.


Receipt OCR

On the new expense form: drag-drop or click the purple AI Receipt Scanner panel to upload a receipt image (JPG/PNG/GIF/WEBP, max 10MB). The AI extracts amount, date, merchant name, and category suggestion, then fills the form fields with a green flash animation. Always review before saving.

On the edit form:

  • Re-extract button re-runs OCR on the already-attached receipt
  • OCR also runs automatically when you select a new image file in the upload field

Teller Bank Sync

Teller connects US bank accounts using a secure mTLS connection. Accessible via Settings → Bank Connections.

Connecting

  1. Go to Settings → Bank Connections
  2. Click Connect a Bank — the Teller Connect modal opens
  3. Select your bank and log in
  4. After connecting, use Map Account on any unmapped accounts to link them to PFM accounts

Syncing Transactions

On the Accounts page, find a Teller-linked account (blue Teller badge) and click Sync. Review the transaction preview and confirm to import. After import, the live balance is fetched from Teller automatically.

Webhook

Teller can push transaction updates to PFM automatically. The webhook endpoint verifies the Teller-Signature header (HMAC-SHA256) and has a 5-minute replay window.

Disconnecting

Settings → Bank Connections → Disconnect. Imported transactions are kept.


Schwab Bank Sync

Schwab integration uses OAuth 2.0 to sync brokerage and IRA accounts. Accessible via Settings.

Connecting

  1. Set SCHWAB_CLIENT_ID, SCHWAB_CLIENT_SECRET, SCHWAB_REDIRECT_URI in .env
  2. Register the redirect URI in the Schwab developer portal (exact match required)
  3. Settings → SchwabConnect Schwab Account
  4. Authorize on Schwab's login page
  5. On the Map Accounts page, link each Schwab account to a PFM account (or create new ones)

Syncing Balance & Investment Positions

On the Accounts page, click Balance & Positions on a Schwab account (green badge). This:

  • Updates the account balance to Schwab's total portfolio liquidation value
  • Upserts stock/ETF/bond/fund holdings into the Investments section
  • Associates each holding with the specific account (Individual vs Roth IRA stay separate)

Also click Sync Schwab in the Investments page topbar to update all Schwab accounts at once.

Syncing Transactions

Click Transactions on a Schwab account card. Review the preview and confirm to import.

Token Expiry

Schwab access tokens expire after 30 minutes (auto-refreshed). Refresh tokens last 7 days — a dashboard warning appears when ≤ 2 days remain. Reconnect via Settings → Schwab to reset the expiry.

Daily Auto-Sync

A cron job runs at 7 AM daily (scripts/sync_schwab.py) — updates balances, positions, and transactions for all mapped Schwab accounts automatically.

Disconnecting

Settings → Schwab → Disconnect. Imported transactions and holdings are kept.


Plaid Bank Sync

Plaid connects to 12,000+ US banks and credit unions. Accessible via Settings.

Connecting

  1. Set PLAID_CLIENT_ID, PLAID_SECRET, PLAID_ENV (sandbox or production) in .env
  2. Settings → PlaidConnect Bank Account
  3. The Plaid Link widget opens — search for your institution and log in
  4. On the Map Accounts page, link each Plaid account to a PFM account (or create new ones)

Syncing Transactions

On the Accounts page, click Sync on a Plaid-linked account (purple badge). This uses cursor-based sync (/transactions/sync) — only new transactions since the last sync are fetched. Review the preview and confirm.

Reset Sync — click Reset to clear the sync cursor. The next sync re-fetches the full available history; duplicates are automatically skipped via the Plaid:<transaction_id> marker in notes.

Live Balance Refresh

Click Refresh on a Plaid account card to fetch the latest balance from Plaid's /accounts/balance/get endpoint.

Credit Card Billing

For Plaid-linked credit cards, click Billing on the account card to fetch:

  • Next payment due date
  • Minimum payment amount
  • Last statement balance
  • Overdue indicator

This information also appears in the billing card on the Accounts page.

Webhook Auto-Import

Plaid can push new transactions to PFM via webhook (POST /plaid/webhook). When a TRANSACTIONS/SYNC event arrives, new transactions are imported silently (no preview step). Transactions imported this way without a category appear in the Plaid review banner on the Transactions page and the sidebar badge — review and categorize them from there.

The webhook is verified using Plaid's JWT signature (ES256). Configure the webhook URL in Settings → Plaid → Apply to Existing Items.

Disconnecting

Settings → Plaid → Disconnect on a connection. Imported transactions are kept.


USD → VND Rate Widget

Reference-only exchange rate on the dashboard. Does not affect any app calculations.

  • Click ↻ to force-refresh without reloading the page
  • Click the widget to expand a 30-day trend chart
  • Source label shown (yfinance / exchangerate-api)
  • ⚠ stale indicator appears if the cached rate is more than one day old

System Logs

Navigate via Settings → System Logs (or sidebar → Settings → System Logs card).

Application log entries are stored in the database and shown in a colour-coded viewer:

  • Filter by level (INFO / WARNING / ERROR / DEBUG) or free-text search
  • Filter by module name (e.g. app.teller, app.plaid)
  • Auto-refresh toggle for live monitoring
  • Download — downloads the raw log file (app.log)
  • Purge dropdown — delete DB entries older than 7, 30, or 90 days
  • Clear All — truncates both the DB log table and the log file
  • DB entry count shown in the page header

Keyboard Shortcuts & Tips

Navigation

  • Sidebar collapses on desktop — click ☰ to toggle (state saved in localStorage)
  • On mobile, tap outside the sidebar to close it; tables scroll horizontally

Transactions

  • Enter sends in AI chat; Shift+Enter adds a new line
  • Change category directly in the transactions table — no form needed
  • Date defaults to today — change it for past transactions
  • Use the ✂ Split button on a row to divide a transaction across multiple categories
  • Export the current filtered view with the CSV / Excel buttons in the filter bar

Budgets

  • Copy from previous month at the start of each month saves re-entering all budgets
  • The Budget vs Actual chart gives a quick visual of where you stand each month
  • Rollover amounts appear as a blue +rollover badge on the row

Investments

  • Click any holding row to go to the detail page with P&L and price history chart
  • button on the new transaction price field fetches the live price for that ticker
  • Ticker symbols are auto-uppercased on save
  • Price alerts banner appears automatically when any holding moves ≥ 5% intraday

Reports

  • Snapshot Now saves today's net worth to the history chart immediately
  • CSV and Excel exports from the Reports page use the selected period
  • From the Transactions page, use the filter bar export buttons to export any filtered view

Security

  • Enable 2FA in Settings → Security for additional login protection
  • The Audit Log (Settings → Security → Audit Log) records every login attempt and security event with IP address
  • Sessions time out after 60 minutes of inactivity