Commit graph

78 commits

Author SHA1 Message Date
archeious
4d9b64d760 feat(groups): render collapsible group details and OOB-swap subtotals
Budget and month pages now wrap sections in native <details> blocks
with summary rows showing the group name and subtotal. Income and
Flexible default open, Committed and Savings default closed so the
day-to-day editing targets are visible and the set-and-forget
commitments collapse out of the way. Primary Debt Target renders
inside the Committed group after Debt Minimums.

Every mutation appends a group-totals partial with OOB spans for
all four group subtotals so the header stays in sync without a
reload regardless of which section changed.

Refs #11

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:44:18 -06:00
archeious
0c533d62ed feat(groups): group views with subtotals for budget and month
budget_group_views composes SectionViews into grouped dataclasses
with a combined total and the default open flag. month_group_views
does the same with planned and applied totals. Group order, labels,
and section-to-group mapping all come from the groups module.

Refs #11

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:44:12 -06:00
archeious
032c35c75e feat(groups): add sinking_fund section and define four-group layout
Section enum gains sinking_fund with label "Sinking Funds". A new
groups module maps each section to one of Income, Committed, Savings,
Flexible and records the default open state per group. The existing
section column is a plain VARCHAR(32) with no CHECK, so no schema
migration is needed to accept the new value.

Refs #11

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:44:09 -06:00
496f44cf8c Gitignore docs/wiki/ (#10) 2026-04-17 12:16:26 -06:00
archeious
671a7405cb chore: gitignore the local wiki checkout
docs/wiki/ is a checkout of quartermaster.wiki.git, not part of the
main repo. Ignoring it keeps git status clean and prevents accidental
cross-repo commits.

Refs #9

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:14:52 -06:00
85efed8f2c Zero Amount header at the top of budget and month pages (#8) 2026-04-17 12:08:49 -06:00
archeious
ce7e0f2b3f test: cover zero-amount math and OOB rendering
Service tests hit empty, positive, negative, and exactly-zero cases,
verify debt_target is excluded from the calculation, and confirm
month_zero responds to applied updates. Route tests assert the zero
widget appears OOB on every mutation with the expected tone class.

Refs #7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:06:45 -06:00
archeious
29c6594347 feat(zero): render Zero Amount header and OOB-swap on every mutation
Single large number on /, Planned + Applied pair on /month/YYYY-MM,
both colour-coded (green at zero, amber positive, red negative).
Every mutation route (add, delete, month entry update) appends the
zero widget to its response with hx-swap-oob so totals stay in sync
without a full page reload. An _append_oob helper replaces the ad-hoc
string concatenation the target card was already using.

Refs #7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:06:42 -06:00
archeious
5a65b4c524 feat(zero): compute zero-amount for budget and month
budget_zero sums income minus every non-income section on the budget
config. month_zero returns both Planned and Applied versions over a
month's entries. zero_tone classifies a value as zero / positive /
negative so templates can pick a colour.

Refs #7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:06:37 -06:00
38c8921885 Monthly budget view with snapshot and applied tracking (#4) 2026-04-17 11:59:08 -06:00
archeious
b2d16120d2 docs: document monthly view, updated layout, and deferred work
Refs #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:30 -06:00
archeious
abdb68a29c test: cover month snapshot, deviation states, and per-month target
Service tests assert that create_month produces origin fields matching
the budget, that edits flip deviation_state to edited, that added rows
are new_in_month, and that a budget entry deleted after snapshot leaves
the month entry unchanged. Route tests exercise the create flow,
applied updates, name edits producing the modified tag, per-month
target isolation, and the malformed-year-month 404.

Refs #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:30 -06:00
archeious
e7354ba8d6 feat(month): add routes, templates, and nav between budget and months
Non-existent months return a page with a single "Create this month"
button; create POSTs return HX-Redirect to the newly-created month.
Each entry row carries three inline HTMX-wired inputs (name, planned,
applied) that trigger on change, posting only the field that changed.
Edits swap the section partial so totals and deviation tags update
together. Deleting a debt minimum in a month also re-renders the
target card via OOB swap. The budget page grows a This-month link and
a month picker; each month page has prev / next / picker / back-to-
config controls.

Refs #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:30 -06:00
archeious
ed038fd974 feat(month): add snapshot service with deviation tracking
create_month copies every budget entry into month_entry with
origin_name/origin_planned retained, resolves the budget's debt target
through source_entry_id to the corresponding MonthEntry, and is
idempotent. deviation_state classifies each row as unchanged, edited,
or new_in_month. Year-month handling (validation, shift across year
boundaries) lives here so the route layer stays thin.

Refs #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:30 -06:00
archeious
58faa9dfe9 feat(db): add Month, MonthEntry, and MonthDebtTarget models with migration
A month is a snapshot of the budget. MonthEntry holds the copied planned
amount plus applied and origin_name/origin_planned so the UI can mark
edited rows. source_entry_id links back to the budget but is nullable
with ON DELETE SET NULL, so deleting a budget row after snapshot leaves
the month intact. MonthDebtTarget is one row per month via CASCADE from
month.

Refs #3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:30 -06:00
6986081ee4 DB backup script and alembic auto-backup hook (#6) 2026-04-17 11:57:19 -06:00
archeious
9f1dd7a914 test: cover backup-db.sh exit paths and slug sanitisation
Asserts soft exit when the source DB is missing, a successful backup
round-trips sqlite rows, a non-file sqlite URL is rejected, the reason
slug defaults to "manual", and a messy reason is sanitised to a safe
filename fragment.

Refs #5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:51:55 -06:00
archeious
9ee934629a docs: document DB safety rule in CLAUDE.md and README
CLAUDE.md states the durable rule: run scripts/backup-db.sh before
any schema change, data migration, or destructive DB operation. The
rule deliberately excludes routine app writes. README summarises
backup location, override env var, and restore procedure.

Refs #5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:51:52 -06:00
archeious
a09860dc61 feat(ops): alembic env hook invokes backup-db.sh before migrations
Runs at env module load so the backup fires ahead of offline and
online migration paths, as well as alembic current / revision. A
failing backup does not stop the migration: this is defense in depth,
not a hard prerequisite, and the common failure case is "DB does not
exist yet" on a fresh checkout.

Refs #5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:51:49 -06:00
archeious
a492294dd7 feat(ops): add backup-db.sh for safe sqlite snapshots
Uses sqlite3.Connection.backup for an online, WAL-safe copy. Resolves
the DB path from QUARTERMASTER_DB_URL or ./quartermaster.db, places
the snapshot in <db-dir>/backups (override with QUARTERMASTER_BACKUP_DIR),
timestamps the filename, and tags it with an optional reason slug.
Absent DB file is a soft exit so the script is safe to call from hooks.

Refs #5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:51:46 -06:00
7d205d2853 Initial scaffold: single-month budget MVP (#2) 2026-04-17 11:31:11 -06:00
archeious
c6cb037f4f feat(ui): stacked tabular layout, drop card look
Sections stack vertically under a fixed-width column. Each
section renders a table with two data columns (name, amount)
plus a delete action. The Primary Debt Target now sits between
Debt Minimums and Food and Essentials and uses a dashed rule to
distinguish it from sections that accept freeform entries.

Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:21:48 -06:00
archeious
94e1571a3f docs: expand README with setup, run, tests, and layout
Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:04:21 -06:00
archeious
3a17dee4ef test: cover CRUD, debt target selection, and ON DELETE SET NULL
Service-level and route-level coverage. Route tests share an
in-memory SQLite engine across threads via StaticPool and
override the get_session dependency.

Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:04:18 -06:00
archeious
c52fa9c470 feat(web): add FastAPI routes, service layer, and HTMX templates
Index view renders one card per section plus the Primary Debt
Target card. Adding or deleting a Debt Minimums entry returns the
section partial plus an out-of-band swap for the target card so
the target dropdown stays in sync without a reload.

Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:04:13 -06:00
archeious
0f5980bd94 feat(db): add Entry and DebtTarget models with initial migration
Entry stores one row per section entry (name, amount, timestamps).
DebtTarget is a singleton table (CHECK id = 1) with a nullable
foreign key to Entry using ON DELETE SET NULL so deleting the
referenced Debt Minimums row clears the pointer.

Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:04:06 -06:00
archeious
cd196c0816 chore: init uv project with FastAPI, SQLAlchemy, Alembic
Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:03:59 -06:00
833a91c2e8 Initial commit 2026-04-17 10:57:47 -06:00