Initial scaffold: single-month budget MVP #2

Merged
claude-code merged 6 commits from feat/1-scaffold into main 2026-04-17 11:31:12 -06:00
Collaborator

Closes #1 (will be manually closed on merge).

Summary

Stands up the quartermaster project end-to-end:

  • uv-managed package with FastAPI, SQLAlchemy, Alembic, Jinja2, pytest
  • SQLAlchemy models for entry (name + amount per section) and debt_target (singleton FK to entry)
  • Alembic migration generating the initial schema
  • FastAPI routes with HTMX partial swaps for add, delete, and target selection
  • HTMX out-of-band swap keeps the Primary Debt Target card in sync when debt minimums change
  • pytest suite covering service logic and HTTP routes (13 tests, all passing)
  • README with setup, run, and test instructions

Sections on the page

Incomes, Fixed Amount Bills, Debt Minimums, Primary Debt Target (pointer), Food and Essentials, Subscriptions, Other. Each section except the debt target accepts name + amount entries and shows a running total.

Schema

entry         (id, section, name, amount, created_at, updated_at)
debt_target   (id = 1, debt_minimum_id FK ON DELETE SET NULL, updated_at)

ON DELETE SET NULL means deleting a targeted debt minimum clears the pointer without cascading. SQLite PRAGMA foreign_keys=ON is set on every connection.

Test plan

  • uv run pytest passes (13/13)
  • uv run alembic upgrade head applies the initial migration against a fresh SQLite file
  • Live server smoke test via curl:
    • / renders all 7 section cards with HTMX + CSS loaded
    • POST /sections/income/entries adds a row and updates the total
    • POST /sections/debt_minimum/entries returns the section partial plus an OOB target card
    • POST /debt-target with a debt_minimum id sets the target; with empty string clears it
    • DELETE /entries/{id} of the targeted debt minimum nulls the pointer and the target card shows "No target selected"
  • Negative amounts are rejected with 400
  • Selecting a non-debt-minimum entry as target returns 400

Out of scope

Multi-month budgets, auth, CSV import/export, currency formatting beyond plain USD.

Closes #1 (will be manually closed on merge). ## Summary Stands up the quartermaster project end-to-end: * uv-managed package with FastAPI, SQLAlchemy, Alembic, Jinja2, pytest * SQLAlchemy models for `entry` (name + amount per section) and `debt_target` (singleton FK to entry) * Alembic migration generating the initial schema * FastAPI routes with HTMX partial swaps for add, delete, and target selection * HTMX out-of-band swap keeps the Primary Debt Target card in sync when debt minimums change * pytest suite covering service logic and HTTP routes (13 tests, all passing) * README with setup, run, and test instructions ## Sections on the page Incomes, Fixed Amount Bills, Debt Minimums, Primary Debt Target (pointer), Food and Essentials, Subscriptions, Other. Each section except the debt target accepts name + amount entries and shows a running total. ## Schema ``` entry (id, section, name, amount, created_at, updated_at) debt_target (id = 1, debt_minimum_id FK ON DELETE SET NULL, updated_at) ``` `ON DELETE SET NULL` means deleting a targeted debt minimum clears the pointer without cascading. SQLite `PRAGMA foreign_keys=ON` is set on every connection. ## Test plan * [x] `uv run pytest` passes (13/13) * [x] `uv run alembic upgrade head` applies the initial migration against a fresh SQLite file * [x] Live server smoke test via curl: * [x] `/` renders all 7 section cards with HTMX + CSS loaded * [x] `POST /sections/income/entries` adds a row and updates the total * [x] `POST /sections/debt_minimum/entries` returns the section partial plus an OOB target card * [x] `POST /debt-target` with a debt_minimum id sets the target; with empty string clears it * [x] `DELETE /entries/{id}` of the targeted debt minimum nulls the pointer and the target card shows "No target selected" * [x] Negative amounts are rejected with 400 * [x] Selecting a non-debt-minimum entry as target returns 400 ## Out of scope Multi-month budgets, auth, CSV import/export, currency formatting beyond plain USD.
claude-code added 5 commits 2026-04-17 11:04:46 -06:00
Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
archeious added 1 commit 2026-04-17 11:21:51 -06:00
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>
claude-code merged commit 7d205d2853 into main 2026-04-17 11:31:12 -06:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: archeious/quartermaster#2
No description provided.