A personal finance web app for tracking income, expenses, and investments in CZK. Built with Django, PostgreSQL (Supabase), and a dark UI served via WhiteNoise.
Repository: github.com/petkov93/finance-tracker
- Overview of balance, total income, and total expenses (all time)
- Recent transactions with edit/delete
- Amounts in CZK
- Add income or expense entries
- Optional category and description
- Categories filtered by type (income vs expense) on the form
- Summary cards (net balance, income, expense counts)
- Monthly bar chart with configurable date range
- Pie charts for expenses and income by category
- Separate view for invested vs profit amounts (CZK)
- Portfolio value (profit − invested) — net gain or loss relative to capital put in
- Same list/edit/delete flow as transactions
- Register, log in, log out
- Each user only sees their own data
- Django admin at
/admin/for categories, transactions, and investment entries
| Layer | Technology |
|---|---|
| Backend | Django 5.x |
| Database | PostgreSQL (Supabase) or SQLite locally |
| Auth | Django built-in users |
| Static files | WhiteNoise |
| Production server | Gunicorn |
| Deploy | Render (render.yaml) |
finance-tracker/
├── config/ # Django settings, URLs, WSGI
├── financetracker/ # Main app
│ ├── management/commands/
│ │ └── seed_categories.py # Default categories (empty DB only)
│ ├── migrations/
│ ├── static/financetracker/css/
│ ├── templates/financetracker/
│ ├── models.py # Category, Transaction, InvestmentEntry
│ ├── views.py
│ ├── forms.py
│ └── urls.py
├── manage.py
├── requirements.txt
├── render.yaml # Render Blueprint
├── run.ps1 # Local dev (Windows)
├── run.sh # Local dev (Linux/macOS)
├── .env.example # Environment template (commit this)
└── .env # Your secrets (never commit)
- Python 3.12+
- A Supabase project (recommended) or SQLite fallback if
SUPABASE_URLis unset
git clone https://github.com/petkov93/finance-tracker.git
cd finance-tracker
python -m venv .venvWindows (PowerShell):
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txtLinux / macOS:
source .venv/bin/activate
pip install -r requirements.txtCopy the example file and fill in your values:
cp .env.example .env| Variable | Description |
|---|---|
SUPABASE_URL |
PostgreSQL connection URI (Supabase → Session pooler) |
SECRET_KEY |
Django secret key (long random string) |
DJANGO_DEBUG |
true for local dev |
Optional locally: ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS (defaults work for localhost).
Supabase URI tip: If your password has special characters, paste the URI as-is; the app URL-encodes it automatically.
Windows:
.\run.ps1Linux / macOS:
bash run.shThis will:
- Run database migrations
- Seed default categories only if the category table is empty
- Collect static files
- Start the dev server at
http://127.0.0.1:8000
Open the app → Sign up, or use the admin:
python manage.py createsuperuserThen visit /admin/.
On first run (empty database), these categories are created automatically:
| Income | Expense |
|---|---|
| Salary 💼 | Food 🍽️ |
| Freelance 💻 | Food at Work 🥪 |
| Other Income 💰 | Health 💊, Transport 🚗, Rent 🏠, … |
If any category already exists, seeding is skipped — your admin changes are never overwritten on restart.
To seed manually:
python manage.py seed_categories- Production / recommended: PostgreSQL on Supabase (
SUPABASE_URLin environment) - Local fallback: SQLite (
db.sqlite3) whenSUPABASE_URLis not set
Migrations:
python manage.py migrate- Push this repo to GitHub (already done if you cloned from petkov93/finance-tracker).
- Render → New → Blueprint → connect the repo (uses
render.yaml). - Set these Environment variables in the Render dashboard:
| Variable | Example |
|---|---|
SUPABASE_URL |
postgresql://... (session pooler) |
SECRET_KEY |
long random string |
DJANGO_DEBUG |
false |
ALLOWED_HOSTS |
your-app.onrender.com |
CSRF_TRUSTED_ORIGINS |
https://your-app.onrender.com |
- Deploy. Render runs migrate, seed (if empty), collectstatic, and Gunicorn.
Health check: GET /health/ → {"status": "ok"}
python manage.py migrate
python manage.py seed_categories
python manage.py collectstatic --noinput
python manage.py createsuperuser
python manage.py runserver
python manage.py check --deploy # production settings checkTests use an in-memory SQLite database (no Supabase required):
python manage.py test --settings=config.settings_testWindows: .\test.ps1
Linux / macOS: bash test.sh
Install dev dependencies first for coverage (see below):
pip install -r requirements-dev.txtRun tests with coverage measurement and a terminal report:
coverage run manage.py test --settings=config.settings_test
coverage reportWindows: .\cover.ps1
Linux / macOS: bash cover.sh
Optional HTML report (open htmlcov/index.html in a browser):
coverage html- Never commit
.env— it is in.gitignore - Use a strong
SECRET_KEYin production - Registration is open by default; restrict via admin or disable
registerURL if you deploy publicly
MIT — see LICENSE.