heezy-finance¶
Last Updated: 2026-08-11 Status: Active Owner: trash
What Is This?¶
heezy-finance is a household spending analytics platform. It is not an accounting tool, it is a visibility tool. The goal is a single dashboard showing where money goes, with accurate data from every spending channel.
Several loosely coupled services write to the same Postgres database (heezy on big-boi,
192.168.1.21:5432), and one Flask app reads from all of them.
Services¶
| Service | Repo Path | URL | Role |
|---|---|---|---|
heezy-finance (dashboard) |
heezy-containers/dockerfiles/heezy-finance/ |
finance.heezy.info | The web app. 9 pages, ~60 API endpoints |
heezy-finance-sync (CronJob) |
same image, amazon_orders.py |
none | Hourly at :00, --hours 2. Gmail order ingest |
heezy-statement-scanner (CronJob) |
same image, scan_statements.py |
none | Hourly at :30. Parses PDFs dropped on NFS |
heezy-budget-alerts (CronJob) |
same image, budget_alerts.py |
none | Daily 14:00 UTC (9am ET). Discord budget warnings |
statements (upload service) |
heezy-containers/dockerfiles/statements/ |
statements.heezy.info | Lands statement files on NFS. No parsing |
receipts |
heezy-containers/dockerfiles/receipts/ |
receipts.heezy.info | Receipt OCR and line-item extraction |
The three CronJobs run the same heezy-finance image with a different command. A dashboard deploy
updates all four at once.
Data Sources¶
Counts as of 2026-08-11.
1. orders / order_items (email-parsed orders)¶
- Source: Amazon and other vendor order confirmation emails
- Populated by:
amazon_orders.py, Gmail API plus Ollamallama3.2:3bon big-boi - Current state: 284 orders, 367 line items, 25 distinct vendors
- Classification: rules, then ASIN/title cache, then LLM. See Data Sources
- Open issue: 7 orders have a NULL or zero
grand_total. Current Amazon HTML does not expose a parseable total on roughly a third of confirmations; line items usually rescue it
2. receipts / receipt_items (scanned receipts)¶
- Source: photos uploaded to receipts.heezy.info
- Populated by: the receipts service, AWS Textract AnalyzeExpense by default
- Current state: 155 receipts, 821 line items
- Open issue: 12 receipts have a NULL
date_tsand are invisible to any date-filtered query
3. bank_statements / bank_transactions (statement parser)¶
- Source: BoA, Capital One, Chase, Edward Jones, Fifth Third PDFs plus Chase CSV/OFX
- Populated by:
parse.py, driven byscan_statements.py(NFS) orPOST /api/statements/upload - Current state: 80 statements, 1,582 transactions
| Institution | Type | Statements | Coverage |
|---|---|---|---|
| Bank of America | checking | 7 | 2026-01 to 2026-07 |
| Bank of America | credit_card | 14 | 2026-01 to 2026-07 |
| Capital One | checking | 10 | 2025-11 to 2026-06 |
| Capital One | savings | 10 | 2025-11 to 2026-06 |
| Chase | credit_card | 14 | 2025-10 to 2026-07 |
| Edward Jones | 529, brokerage, ira, roth_ira, money_market | 24 | 2026-05 to 2026-07 |
| Venmo | venmo | 1 | manual row, no parser |
Fifth Third mortgage statements land in mortgage_statements, not bank_statements. 9 rows.
See Data Sources for parser detail and quirks.
Dashboard¶
- Stack: Flask, vanilla JS, Jinja templates, Plotly for charts
- Live URL: finance.heezy.info
- k8s:
heezynamespace, deploymentheezy-finance, NodePort 30860 - Source:
heezy-containers/dockerfiles/heezy-finance/app.py(~4,000 lines)
Pages¶
Nav order comes from templates/_nav.html.
| Route | Nav label | Doc |
|---|---|---|
/ |
Spending | Overview Tab |
/reconcile |
Reconcile | Reconcile Tab |
/money |
Net Worth | Net Worth Tab |
/targets |
Targets | Targets Tab |
/mortgage |
Mortgage | Mortgage Tab |
/budget |
Budget | Budget Tab |
/statements |
Statements | Statements Tab |
/maintenance |
Maintenance | Toilet paper inventory. Unrelated to spending, and unrelated to the separate heezy-maintenance service |
/bank, /purchases, and /trends are 302s to / (they are tabs inside the Spending SPA).
/sankey is a 302 to /money. /record-balances still renders its own template but is no longer
linked from the nav; the Statements page replaced it.
API Endpoints¶
Grouped. See each page doc for request and response shapes.
| Group | Endpoints |
|---|---|
| Period discovery | /api/available-periods, /api/latest-data-period |
| Spending | /api/overview, /api/transactions, /api/comparison, /api/trends, /api/purchases, /api/bank-overview, /api/category-drilldown, /api/category-transactions/<category>, /api/uncategorized-items |
| Cash flow | /api/sankey |
| Reconcile | /api/reconcile/ + page-data, summary, unmatched-bank, unmatched-receipts, auto-match, link, update-category, acknowledge, unreconciled-summary, unreconciled-by-category |
| Receipts | /api/receipt-detail/<id>, /api/receipts/<id>/items, /api/receipts/<id>/category, /api/receipt-items/<id>, /api/bank-transactions/<id>/stub-receipt |
| Categories | /api/categories, /api/bank-transactions/<id>/category |
| Accounts and net worth | /api/accounts, /api/accounts/<id>/nickname, /api/account-balances/<id>, /api/net-worth, /api/money-history |
| Statements | /api/statements/upload, /api/upload-statement, /api/statements/audit |
| Mortgage | /api/mortgage-latest, /api/mortgage-history, /api/mortgage-calc |
| Targets | /api/targets-summary |
| Budget | /api/budget, /api/budget/<category>, /api/budget/annual, /api/budget/annual/<category>, /api/ytd-by-category |
| Sync | /api/sync |
| Maintenance | /api/tp/bathrooms, /api/tp/inventory, /api/tp/restock, /api/tp/log, /api/tp/stats |
Database¶
All tables in the heezy database on big-boi (192.168.1.21:5432).
Schema source of truth: ansible-heezy/roles/heezy-postgres-schema/files/schema.sql
| Table | Purpose |
|---|---|
orders, order_items |
Order and line-item data from email confirmations |
receipts, receipt_items |
Scanned receipt metadata and line items |
bank_statements, bank_transactions |
Statement metadata and individual transactions |
accounts |
Tracked accounts. Required for balance upsert to succeed |
account_balances |
Point-in-time balance snapshots per account |
mortgage_statements |
Fifth Third statements: balance, rate, principal/interest/escrow split |
spend_budgets |
Monthly per-category budget targets |
annual_budgets |
Yearly per-category budget targets, keyed on (category, year) |
item_category_cache |
ASIN or title-hash to category, so a product classifies once |
statement_files_processed |
Upload dedupe by file hash |
amazon_returns |
Return initiations and acceptances |
home_maintenance |
Home task log with forecasted and actual cost |
tp_bathrooms, tp_inventory, tp_change_log |
Toilet paper tracker behind /maintenance |
vw_sankey_income, vw_sankey_transfers, vw_sankey_spend |
Views. See Sankey Architecture |
Known Issues¶
| Issue | Severity | Notes |
|---|---|---|
12 receipts with NULL date_ts |
Medium | Dropped by every date-filtered query. Backfill SQL in Data Standards |
| Missing account causes silent balance skip | Medium | upsert_account_balance() logs WARN only, no error in the upload response |
7 orders with no grand_total |
Low | Amazon HTML has no parseable total. repair_order_data.py --backfill-totals recovers most from line items |
No FK from statements to accounts |
Low | Matched by institution + last4 + type string at ingest. Renaming an account orphans its history |
bank_statements.statement_period_start/end are text |
Low | Raw PDF strings. period_end_date is the normalized one |
| PVC is RWO on Longhorn | Low | Deploy strategy is Recreate to work around it. Node affinity is gone, so the pod does reschedule |
Resolved since the last revision of this page: the 12 boacrdNone ghost statement rows (now 0), and
the real money-column tech debt (every money column is numeric).