Every section used to end with a wide horizontal add form (name
input, amount input, button, notes input spanning the row). It took
up more horizontal real estate than the entries themselves. Now the
form is wrapped in a <details class="add-entry"> whose summary is a
small tracked-caps link ("+ add fixed amount bills"). Click to
reveal the same form below; HTMX submission still resets and hides
on success.
Same treatment on the budget page sections and the month page
sections.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The notes row was display:none when empty and revealed on entry-row
hover. Moving the cursor down to click the input left the entry row
and immediately hid the notes again, a classic hover-gap. Fix by
always rendering the row with a subtle 0.55 opacity when empty and
bumping it to 1.0 on its own hover or focus. Now the input is
always reachable without a hover dance.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Widen the applied column from 5.5rem to 9rem so "\$134.32 · 7 txns"
fits on one line. Add white-space: nowrap to the cell and its
children as belt-and-braces. Mobile breakpoint gets 7rem with the
count text shrunk, still single-line.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New test_postings.py walks service and route layers: add sums into
applied, negatives are allowed, update and delete round-trip, entry
deletion cascades postings, order is desc by date, update_month_entry
rejects the removed applied kwarg. Route tests assert HTTP behaviour,
invalid-date rejection, closed-month lock, tone flip after a posting,
and the "N txns" count badge renders.
Existing tests that previously set applied via update_month_entry or
the entries route now use add_posting or POST to /postings. Format
assertions updated to match the new thousands-separator number
rendering and the replaced entry-notes-row markup.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each month entry becomes a <details> block. The summary is the same
dense row (name, planned, applied, delete) plus a leading caret and
an applied cell that shows the transaction count ("$412.33 · 7 txns")
when postings exist. Expansion adds no horizontal space.
Expanded body holds: the entry's notes input, a transactions table
with date / description / payee / amount / delete per posting, and
an inline add-transaction form (date, description, payee, amount,
submit). Every field is HTMX-wired so editing any cell triggers the
section partial re-render with fresh derived totals.
Closed month: name / planned / notes / posting fields all collapse
to read-only spans, delete buttons and add forms are omitted. The
existing editable flag controls the branching.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
add_posting / update_posting / delete_posting all go through
ensure_editable(month), so closed months reject posting mutations
with the same lifecycle guard as every other mutation. Negative
amounts are allowed for refunds / corrections. Dates are parsed as
ISO (YYYY-MM-DD) but not constrained to the month for now.
update_month_entry loses the applied keyword; the route no longer
accepts an applied form field. applied is derived only from now
on. Three new routes wire the ledger:
POST /month/{ym}/entries/{entry_id}/postings
POST /month/{ym}/postings/{posting_id}
DELETE /month/{ym}/postings/{posting_id}
Each returns the updated section partial plus OOB swaps for the
zero widget and all four group totals, same pattern the existing
mutations use.
Refs #19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>