Posting is a child of MonthEntry with occurred_on, amount, optional description and payee. Cascade delete so removing an entry wipes its ledger. Ordered on load by occurred_on DESC for readable UIs. MonthEntry.applied becomes a @property summing posting amounts. The stored applied column is dropped in the same migration. The migration walks existing month_entry rows: for every non-zero applied value, it inserts one opening-balance posting on the month's activated_at (or created_at) date with description "opening balance" and amount equal to the existing applied. Empty applied values get no opening posting. Closed months go through the same path; their totals stay intact via that single seeded row. Downgrade is symmetric: re-adds the column and populates from SUM(postings). Refs #19 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| alembic | ||
| docs/mockups | ||
| scripts | ||
| src/quartermaster | ||
| tests | ||
| .gitignore | ||
| .python-version | ||
| alembic.ini | ||
| CLAUDE.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
quartermaster
Household budget tracker. FastAPI + HTMX frontend, SQLite backend.
Pages
/budget configuration. One section per category (Incomes, Fixed Amount Bills, Debt Minimums, Primary Debt Target, Food and Essentials, Subscriptions, Other). Every section accepts name + amount entries and shows a running total. The Primary Debt Target is a pointer to a Debt Minimums row./month/YYYY-MMmonthly view. Snapshots the budget at creation time and tracks anappliedamount per entry alongside the planned amount. Each row is annotated when its name or planned value has been edited away from the snapshot, or when the row was added after creation. Per-month debt target is independent of the budget's target after snapshot.
Navigate between months with the prev / next buttons or the dropdown
picker. A "This month" link on / jumps to the current YYYY-MM; if it
has not been created yet, you land on the create flow.
Requirements
- Python 3.12+
- uv for dependency management
Setup
uv sync
uv run alembic upgrade head
The SQLite file lives at ./quartermaster.db by default. Override with the
QUARTERMASTER_DB_URL environment variable (any SQLAlchemy URL).
Run
uv run uvicorn quartermaster.main:app --reload
Open http://127.0.0.1:8000.
Tests
uv run pytest
Tests run against an in-memory SQLite database; no migration step needed.
Backups
The SQLite data file is precious and gitignored. Before any schema change or destructive operation, back it up:
./scripts/backup-db.sh <reason>
Backups land next to the database in ./backups/ by default
(QUARTERMASTER_BACKUP_DIR=/some/path to override) as
quartermaster-YYYYMMDD-HHMMSS-{slug}.db using SQLite's online backup
API (safe even while the app is writing). Alembic invokes the script
automatically before every migration via alembic/env.py; retention
is forever for now. To restore, stop the app, copy the chosen backup
over quartermaster.db, and restart.
Project Layout
src/quartermaster/
main.py FastAPI app factory
routes.py Budget configuration HTTP handlers
routes_month.py Monthly view HTTP handlers
service.py Budget queries, totals, target logic
month_service.py Snapshot, deviation, per-month CRUD
models.py SQLAlchemy models and Section enum
db.py Engine, session, PRAGMA foreign_keys=ON
config.py DB URL resolution
templates/ Jinja2 templates (base, index, month, partials)
static/ CSS
alembic/ Migrations
tests/ pytest suite
Deferred
A transaction log that rolls up into applied on a per-entry per-month
basis is deferred. Once implemented it may replace the hand-edited applied
field.