Imported from Narsilion/financial-tracker (
AGENTS.md). Install upstream withnpx skills add Narsilion/financial-tracker. Copyright stays with the author.
AGENTS.md
Fast orientation for AI agents working in this repository.
Project Summary
financial-tracker is a local-first personal finance tracker. It is a FastAPI web app backed by a local SQLite database. It tracks people, accounts, cash, dated balance snapshots, exchange rates, dashboard totals, history, Excel import/export, sync JSON export/import, themes, and local backups.
The canonical product direction is in SPECIFICATION.md. The quickest operational overview is in README.md.
Tech Stack
- Python 3.11+
- FastAPI and Uvicorn
- SQLite via the standard
sqlite3module - Pydantic v2 models
openpyxlfor Excel import/exportpytestplus FastAPITestClient- Src-layout package under
src/financial_tracker
Repository Map
src/financial_tracker/app.py: FastAPI app factory and route handlers.src/financial_tracker/db.py: SQLite schema creation, database access, calculations, sync events, account normalization, snapshot revisions.src/financial_tracker/ui.py: Server-rendered HTML, CSS, dashboard/settings/history/snapshot/sync views.src/financial_tracker/schemas.py: Pydantic request/domain input models.src/financial_tracker/settings.py: Environment parsing and project/db path resolution.src/financial_tracker/main.py: CLI entrypoint for starting Uvicorn.src/financial_tracker/importer.py:Family_Budget.xlsx/Funds Serworkbook import.src/financial_tracker/export.py: Excel export builder.src/financial_tracker/backup.py: SQLite backup command and retention pruning.tests/: pytest coverage for DB behavior, API flows, import, backup, and UI output.launchd/: macOS backup task template.windows/: Windows backup task helper..data/: local app data. Treat as runtime/user data, not source.
Local Setup
python3 -m venv .venv
source .venv/bin/activate
python -m pip install ".[dev]"
Installed console scripts:
financial-tracker
financial-tracker-backup
Run the app:
source .venv/bin/activate
financial-tracker
Default URL: http://127.0.0.1:8781
Useful environment variables:
FINANCIAL_TRACKER_HOST=127.0.0.1
FINANCIAL_TRACKER_PORT=8781
FINANCIAL_TRACKER_DB_PATH=./.data/financial-tracker.db
The default live database is .data/financial-tracker.db.
Test Commands
Run all tests:
source .venv/bin/activate
pytest
Run a focused file:
source .venv/bin/activate
pytest tests/test_db.py
The test fixture in tests/conftest.py uses a temporary SQLite DB, so tests should not touch .data/financial-tracker.db.
Data Model Notes
Database.initialize() in db.py creates and migrates the current schema opportunistically with CREATE TABLE IF NOT EXISTS statements.
Core tables:
peoplecurrenciesaccountssnapshotssnapshot_revisionsbalance_entriesexchange_ratesdevicessync_eventsapp_settings
Supported currencies are currently fixed to RSD, EUR, and USD; RSD is the default base currency.
Snapshots are revision-based. Saving the same snapshot_date again creates a new snapshot revision and marks it current. Calculations should use the current revision.
Money values are stored as text in SQLite and normalized through Decimal; avoid floats for financial calculations.
Cash entries use entry_type="cash" and have no account_id. Account entries use entry_type="account" and should have an account.
Default accounts are created for every new person by ensure_default_bank_accounts: main and savings accounts for RSD, EUR, and USD.
Account names are normalized by normalize_account_name; do not assume user-submitted account labels are stored verbatim.
App Routes
Main routes in app.py:
GET /: dashboard.GET /health: health check.GET /settings: people/accounts/theme settings.POST /settings/theme: save theme.POST /people,POST /people/{person_id}: create/update people.POST /accounts,POST /accounts/{account_id}: create/update accounts.GET /snapshot/new: snapshot entry form.POST /snapshots: save snapshot or new revision.GET /snapshots/{snapshot_id}/revisions: revision history page.GET /history: filterable history table.GET /export.xlsx: Excel export.POST /import/funds-ser: import workbook.GET /sync,GET /sync/export,POST /sync/import: basic sync views/actions.
UI Notes
The app is server-rendered from ui.py; there is no separate frontend build step.
When changing UI behavior, update tests that assert generated HTML in tests/test_api.py and tests/test_ui.py.
Themes are stored in app_settings.theme. Valid themes currently include dark, light, brown-dark, and dark-green.
The dashboard has filters for person, account, bank, detail currency, and total currency. Preserve query parameter behavior and anchors such as #current-balance-details unless intentionally changing navigation.
Import Notes
importer.py targets an Excel sheet named Funds Ser from Family_Budget.xlsx.
Important importer behavior:
- Expected current snapshot date is read from cell
A1. - EUR and USD rates are read around rows 36 and 37.
- It knows about
MariaandAlex, and can reuse existing Cyrillic peopleМашаandСаша. - A shared cash block is assigned to Maria to fit the two-person data model.
- Historical EUR totals are imported into a synthetic
Imported historical totalaccount, which is then deactivated.
Keep importer tests close to the spreadsheet row/column assumptions.
Backup Notes
financial-tracker-backup uses SQLite's backup API and writes dated .db files to .data/backups/ by default.
Retention currently keeps the newest N backup files, controlled by --retention-days even though the implementation counts files rather than calendar days.
Do not delete user backups unless explicitly requested.
Development Guardrails
- This repository may have uncommitted user changes. Check
git status --shortbefore edits and do not revert unrelated changes. - Treat
.data/financial-tracker.dband.data/backups/as user/runtime data. - Prefer adding focused tests for changed DB calculations, route behavior, importer parsing, and rendered HTML.
- Use
Decimalfor money and preserve two-decimal normalization viamoney(). - Keep schema changes backward-compatible where possible because
initialize()runs against existing local databases. - Avoid broad refactors in
db.py; it is the central behavioral surface and many tests depend on exact aggregation semantics. - Keep UI changes server-rendered unless the project explicitly gains a frontend pipeline.
- Use
rg/rg --filesfor discovery.
Common Workflows
Add or adjust a route:
- Update
app.py. - Add or reuse DB methods in
db.py. - Render in
ui.py. - Cover route behavior in
tests/test_api.py.
Change dashboard/history calculations:
- Start in
db.py. - Check affected render assumptions in
ui.py. - Add DB-level tests in
tests/test_db.py. - Add API/HTML assertions if user-visible output changes.
Change importer behavior:
- Update
importer.py. - Extend workbook fixture setup in
tests/test_importer.py. - Verify generated people/accounts/snapshots/rates.
Change backup behavior:
- Update
backup.py. - Extend
tests/test_backup.py. - Avoid touching real
.data/backups/in tests.
Current Behavior Worth Preserving
- Latest snapshot by date drives dashboard current totals.
- Previous snapshot comparison uses the immediately preceding snapshot.
- Re-saving a snapshot date creates a revision, not a second visible snapshot row.
- Inactive imported historical accounts are excluded from native currency current-balance charts but can still contribute to converted historical totals where intended.
- Cash can be shown as common/everyone in aggregated current-balance views.
- Default DB path is relative to repo root when launched from this project.