06/06 Optimize app
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 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 or Schwab.
|
||||
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
|
||||
|
||||
@@ -20,13 +20,16 @@ A self-hosted personal finance web application. Track income, expenses, investme
|
||||
10. [AI Assistant](#ai-assistant)
|
||||
11. [Reports & Export](#reports--export)
|
||||
12. [Settings](#settings)
|
||||
13. [Recurring Transactions](#recurring-transactions)
|
||||
14. [CSV Import](#csv-import)
|
||||
15. [Receipt OCR](#receipt-ocr)
|
||||
16. [Teller Bank Sync](#teller-bank-sync)
|
||||
17. [Schwab Bank Sync](#schwab-bank-sync)
|
||||
18. [USD → VND Rate Widget](#usd--vnd-rate-widget)
|
||||
19. [Keyboard Shortcuts & Tips](#keyboard-shortcuts--tips)
|
||||
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)
|
||||
|
||||
---
|
||||
|
||||
@@ -34,19 +37,22 @@ A self-hosted personal finance web application. Track income, expenses, investme
|
||||
|
||||
| Module | What it does |
|
||||
|--------|-------------|
|
||||
| **Dashboard** | Net worth, cash flow chart, budget gauges, AI insight, USD/VND rate |
|
||||
| **Transactions** | Income + expense entry, transfer, filter, search, inline category change, receipt upload |
|
||||
| **Accounts** | Multiple bank/cash/credit accounts, auto-calculated or provider-synced balances |
|
||||
| **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 per category with progress tracking |
|
||||
| **Goals** | Savings goals with contribution tracking and projected completion |
|
||||
| **Investments** | Stock/ETF/crypto/real estate portfolio, per-account view (Individual vs Roth IRA), live price fetch |
|
||||
| **AI Assistant** | Chat with your finances via Groq (free), daily auto-insight |
|
||||
| **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, PDF/CSV/Excel export |
|
||||
| **Teller** | Live US bank account sync (mTLS), balance refresh, transaction import |
|
||||
| **Schwab** | Schwab brokerage sync — balance, stock/ETF positions, transaction import |
|
||||
| **Settings** | Profile, currency, recurring rules, CSV import, password |
|
||||
| **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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -61,13 +67,14 @@ A self-hosted personal finance web application. Track income, expenses, investme
|
||||
### Recommended Setup Order
|
||||
|
||||
1. **Add accounts** — add your bank accounts, cash wallet, and credit cards first
|
||||
2. **Review categories** — default categories are already seeded; add custom ones if needed
|
||||
3. **Set your currency** — go to Settings → Profile and set your app currency
|
||||
4. **Connect bank sync** — connect Teller (US banks) or Schwab for automatic data
|
||||
5. **Add transactions** — start entering income and expenses (or import via bank sync)
|
||||
6. **Set budgets** — once you have categories, set monthly limits
|
||||
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
|
||||
|
||||
---
|
||||
|
||||
@@ -76,80 +83,131 @@ A self-hosted personal finance web application. Track income, expenses, investme
|
||||
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).
|
||||
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
|
||||
- **Net Worth** — total assets minus liabilities across all accounts
|
||||
- **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 the **Amount Owed** (positive number) instead of a raw negative balance. Green = positive balance, red = amount owed.
|
||||
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. Click "Open AI →" for the full chat interface.
|
||||
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. Click the widget to show a 30-day chart.
|
||||
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**.
|
||||
Navigate via **Transactions** in the sidebar. Two tabs: **Expenses** and **Income** (with total counts).
|
||||
|
||||
### Filtering
|
||||
Search bar, category dropdown, account dropdown, date range. Click ✕ to clear all filters.
|
||||
- **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 sidebar links (Add Income / Add Expense), the dashboard quick-add buttons, or the topbar buttons on the transaction list.
|
||||
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
|
||||
On any transaction row, click the **category dropdown** directly in the table to change the category without opening the edit form. The change saves automatically.
|
||||
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 (same as adding, plus receipt management). Click **Del** to delete permanently.
|
||||
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 between two accounts. Transfers are excluded from income/expense totals.
|
||||
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
|
||||
`checking` / `savings` / `cash` / `credit_card` / `crypto` / `investment` / `other`
|
||||
|
||||
### Balance Sources
|
||||
- **Manually managed accounts** — balance is computed from all transactions (income − expenses − transfers out + transfers in). Updates automatically after every transaction.
|
||||
- **Teller-linked accounts** — balance comes from Teller's live API. Shown with a blue **Teller** badge. Use the action buttons on the account card.
|
||||
- **Schwab-linked accounts** — balance comes from Schwab's snapshot sync. Shown with a green **Schwab** badge. Use the action buttons on the account card.
|
||||
- **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 (on account card)
|
||||
### Teller Account Actions
|
||||
| Button | What it does |
|
||||
|--------|-------------|
|
||||
| **Refresh** | Pulls the live balance from Teller API (AJAX, no page reload) |
|
||||
| **Sync** | Opens transaction preview — import new transactions from this account |
|
||||
| **Reset** | Clears the sync cursor — next Sync will re-fetch the full 90-day history |
|
||||
| **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 (on account card)
|
||||
### Schwab Account Actions
|
||||
| Button | What it does |
|
||||
|--------|-------------|
|
||||
| **Balance & Positions** | Fetches live balance + investment holdings from Schwab, updates immediately |
|
||||
| **Transactions** | Opens transaction import preview for this account |
|
||||
| **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 in red)
|
||||
- **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 debt (e.g. `-500` if you owe $500).
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
@@ -161,7 +219,7 @@ Navigate via the sidebar footer → **Categories**.
|
||||
|
||||
**Default Income (7):** Salary, Freelance, Business, Investment, Rental, Gift Received, Other Income
|
||||
|
||||
System categories (grey badge) cannot be deleted but color/icon can be changed. Custom categories can be fully edited and deleted.
|
||||
System categories (grey badge) cannot be deleted — color and icon can still be changed. Custom categories can be fully edited and deleted.
|
||||
|
||||
---
|
||||
|
||||
@@ -169,10 +227,20 @@ System categories (grey badge) cannot be deleted but color/icon can be changed.
|
||||
|
||||
Navigate via **Budgets** in the sidebar. Use ◀ ▶ to navigate months.
|
||||
|
||||
- Set monthly limits per expense category
|
||||
- Progress bars: green (< 80%) → amber (80–99%) → red (100%+)
|
||||
- Rollover toggle: unspent budget carries forward to next month
|
||||
- **Copy from previous month** — duplicate last month's budgets in one click
|
||||
### 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 (80–99%) → 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.
|
||||
|
||||
---
|
||||
|
||||
@@ -181,9 +249,10 @@ Navigate via **Budgets** in the sidebar. Use ◀ ▶ to navigate months.
|
||||
Navigate via **Goals** in the sidebar.
|
||||
|
||||
- Create goals with target amount, target date, color, and icon
|
||||
- Add contributions with dates and notes
|
||||
- Auto-completes when target is reached
|
||||
- **Emergency Fund Tracker** — shows how many months your liquid assets cover at current spending
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
@@ -192,25 +261,33 @@ Navigate via **Goals** in the sidebar.
|
||||
Navigate via **Investments** in the sidebar.
|
||||
|
||||
### Portfolio Overview
|
||||
Summary cards: Total Value, Total Cost, Unrealized P&L, Return %. Allocation doughnut chart by asset type.
|
||||
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 you have investments in multiple accounts (e.g. Schwab Individual + Schwab Roth IRA), the holdings are displayed in **separate sections**, one per account. Each section shows the account name, total account value, and the holdings table for that account. The allocation chart sidebar also shows a "By Account" balance breakdown.
|
||||
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. After syncing, each holding is associated with its source account so Individual and Roth IRA positions stay separate even if they hold the same tickers.
|
||||
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: `AAPL`, `MSFT`
|
||||
- ETFs: `VOO`, `QQQ`
|
||||
- US Stocks/ETFs: `AAPL`, `VOO`, `QQQ`
|
||||
- Crypto: `BTC-USD`, `ETH-USD`
|
||||
- Other markets: Yahoo Finance suffix (e.g. `VIC.VN`)
|
||||
- International: Yahoo Finance suffix (e.g. `VIC.VN`)
|
||||
|
||||
### Refreshing Prices
|
||||
Click **Refresh Prices** in the topbar. Prices also auto-update daily at 4PM (weekdays).
|
||||
Click **Refresh Prices** in the topbar. Prices also auto-update daily at 4 PM weekdays via cron.
|
||||
|
||||
---
|
||||
|
||||
@@ -218,15 +295,18 @@ Click **Refresh Prices** in the topbar. Prices also auto-update daily at 4PM (we
|
||||
|
||||
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 identifying details are sent to Groq — only aggregated financial figures.
|
||||
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 and press **Enter** to send; **Shift+Enter** for new line
|
||||
- Eight quick-question suggestion buttons on the right
|
||||
- **Daily Insight** — auto-generated summary at midnight; click **Generate Now** to create on demand
|
||||
- **Chat History** button — all past responses with timestamps
|
||||
- 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) or `llama-3.1-8b-instant` (faster).
|
||||
Change in Settings → Profile → AI Model:
|
||||
- `llama-3.3-70b-versatile` — best quality (default)
|
||||
- `llama-3.1-8b-instant` — faster, slightly lower quality
|
||||
|
||||
---
|
||||
|
||||
@@ -236,9 +316,14 @@ Navigate via **Reports** in the sidebar.
|
||||
|
||||
Four tabs: **Monthly**, **Quarterly**, **Yearly**, **Tax Year**.
|
||||
|
||||
Export buttons in the topbar: **CSV** (plain text), **Excel** (color-coded, formatted), **PDF** (printable report).
|
||||
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
|
||||
|
||||
**Snapshot Now** — saves today's net worth to the history chart.
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
@@ -248,10 +333,44 @@ Navigate via the gear icon at the bottom of the sidebar.
|
||||
|
||||
| Section | What you can change |
|
||||
|---------|-------------------|
|
||||
| Profile | Display name, email, timezone, currency, AI model |
|
||||
| Password | Current password + new password (min 6 chars) |
|
||||
| Recurring | Create/edit/pause recurring transaction rules |
|
||||
| Import | Upload CSV to bulk-import transactions |
|
||||
| **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.
|
||||
|
||||
---
|
||||
|
||||
@@ -259,78 +378,140 @@ Navigate via the gear icon at the bottom of the sidebar.
|
||||
|
||||
Navigate via Settings → **Recurring**.
|
||||
|
||||
Rules auto-create transactions on a schedule. Frequencies: daily, weekly, biweekly, monthly, quarterly, yearly. **Run Now** button processes all overdue rules immediately.
|
||||
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
|
||||
|
||||
---
|
||||
|
||||
## CSV Import
|
||||
## Bank Statement Import
|
||||
|
||||
Navigate via Settings → **Import**.
|
||||
Navigate via Settings → **Import Statement** (sidebar) or Settings → Import.
|
||||
|
||||
**Required columns:** `date`, `type` (income/expense), `description`, `amount`
|
||||
### 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 |
|
||||
|
||||
**Optional:** `category`, `account`, `notes`
|
||||
### 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
|
||||
|
||||
Process: upload → preview (with warnings for unmatched categories/accounts) → confirm.
|
||||
### 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 panel to upload a receipt image (JPG/PNG/GIF/WEBP, max 10MB). The AI extracts amount, date, merchant, and category and fills the form. Always review before saving.
|
||||
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: click **Re-extract** to re-run OCR on an already-attached receipt.
|
||||
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.
|
||||
Teller connects US bank accounts using a secure mTLS connection. Accessible via **Settings** → Bank Connections.
|
||||
|
||||
### Connecting
|
||||
1. Go to **Bank Connections** in the sidebar
|
||||
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, go to the **Map Accounts** page to link each Teller account to a PFM account
|
||||
4. After connecting, use **Map Account** on any unmapped accounts to link them to PFM accounts
|
||||
|
||||
### Syncing Transactions
|
||||
On the **Accounts** page, find your Teller-linked account (blue **Teller** badge) and click **Sync**. Review the transaction preview and confirm to import.
|
||||
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.
|
||||
|
||||
### Refreshing Balance
|
||||
Click **Refresh** on a Teller account card to pull the live balance from your bank. For checking/savings this shows the available balance; for credit cards it shows the amount owed.
|
||||
### 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
|
||||
Go to **Bank Connections** → **Disconnect** next to the institution. Imported transactions are kept.
|
||||
Settings → Bank Connections → **Disconnect**. Imported transactions are kept.
|
||||
|
||||
---
|
||||
|
||||
## Schwab Bank Sync
|
||||
|
||||
Schwab integration uses OAuth 2.0 to sync brokerage and IRA accounts.
|
||||
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
|
||||
3. Go to **Schwab** in the sidebar → **Connect Schwab Account**
|
||||
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, find your Schwab-linked account (green **Schwab** badge) and click **Balance & Positions**. This:
|
||||
- Updates the account's balance to Schwab's total portfolio value
|
||||
- Imports all stock/ETF/bond/fund holdings into the Investments section
|
||||
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)
|
||||
|
||||
You can also click **Sync Schwab** in the Investments page topbar to update all Schwab accounts at once.
|
||||
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.
|
||||
|
||||
### Per-Account Investment View
|
||||
After syncing both Individual and Roth IRA accounts, the Investments page shows separate sections for each account — even if both hold the same ticker (e.g. AAPL appears in both sections independently).
|
||||
### 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
|
||||
Go to **Schwab** in the sidebar → **Disconnect**. Imported transactions and investment holdings are kept.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@@ -339,32 +520,57 @@ Go to **Schwab** in the sidebar → **Disconnect**. Imported transactions and in
|
||||
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 show a 30-day trend chart
|
||||
- ⚠ indicator appears if the cached rate is more than a day old
|
||||
- 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 remembered)
|
||||
- On mobile, tap outside the sidebar to close it
|
||||
- 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 — click the category dropdown on any row
|
||||
- Date defaults to today; change it for past transactions
|
||||
|
||||
### Investments
|
||||
- Click any row in the holdings table to go to the detail page
|
||||
- **↓** button on the transaction price field fetches the current live price
|
||||
- Ticker symbols are auto-uppercased on save
|
||||
- **Sync Schwab** (topbar) syncs all Schwab accounts at once
|
||||
- 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** button at the start of each month saves re-entering all budgets
|
||||
- Rollover amounts appear as a blue "+rollover" badge
|
||||
- **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** button saves today's net worth to the history chart
|
||||
- CSV/Excel exports use the period currently selected in the report view
|
||||
- **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
|
||||
|
||||
@@ -316,20 +316,6 @@
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="{{ url_for('transactions.new', type='income') }}"
|
||||
class="sb-link"
|
||||
>
|
||||
<i class="bi bi-arrow-down-circle"></i
|
||||
><span class="lt">Add Income</span>
|
||||
</a>
|
||||
<a
|
||||
href="{{ url_for('transactions.new', type='expense') }}"
|
||||
class="sb-link"
|
||||
>
|
||||
<i class="bi bi-arrow-up-circle"></i
|
||||
><span class="lt">Add Expense</span>
|
||||
</a>
|
||||
<a
|
||||
href="{{ url_for('accounts.index') }}"
|
||||
class="sb-link {% if request.blueprint == 'accounts' %}active{% endif %}"
|
||||
|
||||
Reference in New Issue
Block a user