A Python engine (extraction, validation, analysis, anomaly detection, incident
triage, reporting), exposed through a FastAPI backend, with a Next.js/
TypeScript frontend. The original Streamlit app still works and lives in
streamlit_legacy/ as a fallback — all three share the same database.
Next.js frontend --HTTP/JSON--> FastAPI backend --> engine/ --> SQLite
\-> invoice PDFs (data/uploads)
Terminal 1 — backend (from invoice_app/):
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
uvicorn backend.main:app --reload --port 8000Terminal 2 — frontend (from invoice_app/frontend/):
npm install
npm run dev # http://localhost:3000Optional — Streamlit fallback (from invoice_app/, same venv):
streamlit run streamlit_legacy/app.py # http://localhost:8501All three read/write data/invoice_review_demo.db and data/uploads/, so
data created in one shows up in the others immediately.
| Route | What it does |
|---|---|
/overview |
Metric strip, ranked Needs Attention table, portfolio trend + category charts, top properties by excess, recent actions |
/triage |
Compact incident table (filter/search/sort) + right-side detail panel with Dismiss / Confirm / Investigate / Create Action |
/properties |
Property list, add new property |
/properties/[id] |
Header (units, occupancy, spend, YoY, open incidents) + 5 sub-tabs: Summary (trend chart, click a point to open its invoice), Utilities, Invoices, Occupancy, Findings & Actions |
/invoices |
Queue: search, state filter, batch-approve for clean high-confidence invoices, upload |
/invoices/[id] |
Two-column review workspace: PDF viewer / grouped field form with inline confidence + validation + duplicate comparison, sticky approve bar, previous/next controls |
/analysis |
Ad hoc property/category/month lookup with trend chart, for digging into anything not on a property's curated page |
/findings |
Findings (status workflow, create actions inline) and Actions (owner/due date/expected-vs-confirmed savings) in two tabs |
/settings |
Anomaly thresholds, occupancy import (preview + validation before commit), backup/export/report generation, dismissed-patterns management |
Every page above was verified two ways, the same discipline used throughout this project:
npm run buildpasses — real TypeScript errors were caught and fixed along the way (a Recharts v3 click-handler type change, a couple ofnumber | nullmismatches), not just written and assumed correct.- Every endpoint each page depends on was hit directly (via
curl) with the exact payload shape the corresponding component constructs, confirming the JSON response matches the TypeScript interface field-for- field. This covers: overview aggregation, invoice upload/update/approve/ audit, incident confirm/dismiss/create-action, finding status updates, action creation/updates, property detail/trend/occupancy, and settings (thresholds, occupancy import preview+commit, backup, report generation in md/csv/pdf).
What this doesn't cover: real browser/DOM rendering. Playwright and Puppeteer both fetch their Chromium binaries from CDNs outside this environment's network allowlist, so no headless-browser pass was possible. The build type-checks and every API contract is verified against live data, but nobody has watched these pages actually render and clicked through them in a real browser. That's the one gap left for a human to close.
invoice_app/
├── engine/ # Pure Python — no Streamlit, no FastAPI
├── backend/ # FastAPI: main.py, routers/, schemas/, services/
├── frontend/ # Next.js — one folder per route under app/
│ ├── components/ui/ # Button, Input, StatusPill, Surface, MetricCard, StickyActionBar
│ ├── components/layout/# Sidebar
│ └── lib/ # api.ts (typed client), utils.ts, query-provider.tsx
├── streamlit_legacy/app.py
├── data/ # Shared SQLite db + uploaded PDFs (created on first run)
└── tests/ # 37 engine tests
- Vendor-specific extraction patterns — the parser is still one generic regex set.
- Weather sensitivity in baselines (heating/cooling degree days).
- Report review-before-finalization — reports generate and download immediately, no draft/approve step.
- Auth + PostgreSQL — deliberately not started, matching the plan's own stated trigger conditions (multiple simultaneous users, hosted deployment, larger portfolios) — none of which apply yet.
- shadcn/ui's CLI couldn't run (
ui.shadcn.comis outside this environment's network allowlist); the underlying Radix UI primitives were installed directly via npm instead. Functionally equivalent, just not literally shadcn's generated component files.