A private account of income & outflow.
A deliberately small, single-user personal finance tracker. Record income and expenses — both categorized — and watch a quiet editorial dashboard keep your books balanced. Built with Flask + SQLite, styled with a real Tailwind CSS build step. No accounts, no login, no cloud. Just open it and write in the ledger.
- Dashboard — income, outflow, and balance at a glance, a Where it Went category donut, and your most recent entries.
- General Ledger — the complete record of every transaction, filterable by income / outflow.
- Categories — manage your own income and expense categories. Defaults are seeded on first run; categories in use can't be deleted.
- Locale-aware currency — amounts render as proper Indian Rupees
(
₹1,23,456.78) via Babel/CLDR. - PRG + flash messages — every action redirects with a clear success/error note.
Summary cards, a spend-by-category breakdown, an inline Record an Entry form, and recent entries — all on one page.
The full transaction history, filterable by income or outflow.
Income and expense categories, side by side, each with its own add form.
| Layer | Choice |
|---|---|
| Backend | Flask 3 |
| Database | SQLite via Flask-SQLAlchemy |
| Currency | Babel (CLDR en_IN formatting) |
| Styling | Tailwind CSS 3 (npm build step, not CDN) |
| Fonts | Fraunces · Hanken Grotesk · IBM Plex Mono |
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtnpm install
npm run build:css # or: npm run watch:css (rebuilds on change)python app.pyVisit http://127.0.0.1:5000. The SQLite database (instance/expense.db) and
default categories are created automatically on first run.
Tip: during development, run
npm run watch:cssin one terminal andpython app.pyin another.
Two tables keep the whole thing honest:
- Category —
name,type(income|expense). A category belongs to one kind, so the entry form only offers relevant choices. (name+typeare unique.) - Transaction —
type,amount,category_id(required),description,date.
On first launch the app seeds:
- Income — Salary, Pocket Money, Other
- Expense — Food, Transport, Bills, Shopping, Other
| Method | Path | Purpose |
|---|---|---|
| GET | / |
Dashboard (totals, breakdown, recent 10) |
| GET | /transactions |
Full ledger (?type=income|expense) |
| POST | /transactions/add |
Add an income or expense |
| POST | /transactions/<id>/delete |
Delete a transaction |
| GET | /categories |
Manage categories |
| POST | /categories/add |
Create a category |
| POST | /categories/<id>/delete |
Delete a category (blocked if in use) |
simple_expense/
├── app.py # Flask app, models, routes (single file — it's small)
├── requirements.txt
├── package.json # Tailwind build scripts
├── tailwind.config.js
├── templates/
│ ├── base.html # masthead, nav, flash messages
│ ├── index.html # dashboard
│ ├── transactions.html # general ledger
│ ├── categories.html # category management
│ └── _tx_table.html # shared transaction table partial
├── static/
│ ├── src/input.css # @tailwind directives
│ └── css/output.css # built CSS (gitignored)
└── instance/expense.db # SQLite database (created at runtime, gitignored)
Balanced books, quiet mind.


