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

577 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](#features-overview)
2. [Getting Started](#getting-started)
3. [Dashboard](#dashboard)
4. [Transactions](#transactions)
5. [Accounts](#accounts)
6. [Categories](#categories)
7. [Budgets](#budgets)
8. [Goals & Savings](#goals--savings)
9. [Investments](#investments)
10. [AI Assistant](#ai-assistant)
11. [Reports & Export](#reports--export)
12. [Settings](#settings)
13. [Security & 2FA](#security--2fa)
14. [Recurring Transactions](#recurring-transactions)
15. [Bank Statement Import](#bank-statement-import)
16. [Receipt OCR](#receipt-ocr)
17. [Teller Bank Sync](#teller-bank-sync)
18. [Schwab Bank Sync](#schwab-bank-sync)
19. [Plaid Bank Sync](#plaid-bank-sync)
20. [USD → VND Rate Widget](#usd--vnd-rate-widget)
21. [System Logs](#system-logs)
22. [Keyboard Shortcuts & Tips](#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
### Recommended Setup Order
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 → **Schwab****Connect 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 → **Plaid****Connect 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