Skip to content

About

Budget Monitor Web version

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Budget Monitor — Web

A web client for Budget Monitor, sharing the same Supabase project as the Qt desktop app for cross-platform data. Built with React + Vite + supabase-js, plus a thin Node parsing backend for bank-statement imports.

┌─────────────────────┐         ┌──────────────────────────┐
│  React + Vite SPA   │         │  Qt Desktop app          │
│  localhost:5173     │         │  (unchanged)             │
│  supabase-js +      │         │  QPSQL direct connection │
│  anon key + RLS     │         └────────────┬─────────────┘
└──────┬──────────────┘                      │
       │ REST (CRUD, auth, reports)          │ direct Postgres
       ▼                                     ▼
        ┌────────────────────────────────────────┐
        │   Supabase Postgres  (SAME PROJECT)     │
        └────────────────────────────────────────┘
       ▲
       │ inserts with the CALLER's access token (anon key + RLS)
┌──────┴──────────────┐
│  Parser backend     │  Express on localhost:3000
│  localhost:3000     │  upload PDF/XLSX/CSV → parse → insert
└─────────────────────┘

⚠️ Step 0 — Enable RLS (do this first, once)

The web app uses the anon key, so Row Level Security is mandatory. Open the Supabase dashboard → SQL Editor and run supabase/migrations/0001_enable_rls.sql. This does not affect the desktop app (its privileged role bypasses RLS).

Then apply the remaining migrations in supabase/migrations/ in filename order.

Run it

# 1. Frontend
cd frontend
npm install
npm run dev            # → http://localhost:5173

# 2. Parser backend (separate terminal, only needed for file imports)
cd backend
npm install
npm run dev            # → http://localhost:3000

Environment

Create frontend/.env:

VITE_SUPABASE_URL=https://YOUR-PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

and backend/.env:

PORT=3000
SUPABASE_URL=https://YOUR-PROJECT.supabase.co
SUPABASE_ANON_KEY=your-anon-key
PDF_PARSER_CMD=          # optional; path to the Python used for PDF parsing

Both sides use the anon key — there is no service-role key anywhere in this project. See Security notes.

PDF imports additionally need the Python dependencies:

pip install -r backend/parsers/requirements-pdf.txt

Statement imports

Upload on the Import page; the file extension picks the parser (backend/parsers/index.js). Each Chilean bank exports a different, position-based layout, so there is one parser per bank rather than a generic reader.

Bank Spreadsheet (.xlsx / .xls / .csv) PDF
BICE debit, credit (national) debit
Banco de Chile debit, credit debit, credit
BancoEstado debit debit
Santander debit debit
Wise debit —

Imports are deduplicated on date + amount + account + original_description. original_description is frozen at import and never rewritten, so re-importing a statement collapses onto existing rows even after you have edited a row's description. Category is deliberately excluded from that key, so recategorizing a transaction never causes a re-import to duplicate it.

Installments (cuotas)

An installment purchase is expanded at import into one transaction per remaining installment, dated a month apart, each labelled (n/total) in its description.

BICE credit states the purchase total in its Monto column, so each installment bills Monto / total, with the integer-CLP remainder on the earliest cuotas. Banco de Chile instead states the per-cuota amount already, so that parser repeats it unchanged — the two are not interchangeable.

The per-cuota amount and the (n/total) label are both derived from the installment's absolute index in the plan, so a given cuota looks identical no matter which month's statement projected it, and next month's import dedups against it instead of inserting a second copy.

This means the credit view's card usage is the sum of every cuota still to be billed, while each monthly payment is only the cuotas falling due that month. A card payment (MONTO CANCELADO) settles an earlier statement, so it is excluded from both figures rather than netted against charges it never covered.

What maps to what

Qt desktop Web equivalent
mainwindow App.jsx + routing
Supabase auth (Qt REST) supabase.auth.* in auth/
QSqlTableModel reads supabase.from(...).select()
add/edit dialogs components/TransactionEditor.jsx
MonthlyReportService src/lib/reports.js
libs/ C++ parsers backend/parsers/
SimpleClassifier backend/parsers/classifier.js (XLSX/CSV)

The PDF path uses a separate classifier ported in statement_extract.py, which mirrors the desktop's own split and uses slightly different category names for some buckets.

Security notes

  • Everything uses the anon key, front and back. It is safe in the browser only because RLS is enabled — do Step 0 first.
  • The backend does not hold elevated credentials. It trusts the user's access token (sent as a Bearer header), resolves the user with it, and performs every insert through a client scoped to that token, so RLS enforces ownership. A request without a valid token is rejected before any parsing happens.
  • If you ever introduce a service-role key, it must stay server-side and never be returned to the browser — it bypasses RLS entirely.
  • Never commit real .env files or bank statements. .gitignore excludes every .env* variant (including timestamped backups like .env.bak.20260730, which hold live keys) and all .pdf / .xlsx / .xls / .csv statement samples, which are real personal financial data.

About

Budget Monitor Web version

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages