Skip to content

chore: migrate to ferro-orm 0.12.x - #12

Open
0x054 wants to merge 3 commits into
mainfrom
chore/ferro-0-12-migration
Open

chore: migrate to ferro-orm 0.12.x#12
0x054 wants to merge 3 commits into
mainfrom
chore/ferro-0-12-migration

Conversation

@0x054

@0x054 0x054 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Bump ferro-orm to >=0.12.3,<0.13 and adopt the v0.12 session + query APIs
  • Replace auto_migrate + hand-rolled _COLUMN_ADDS shim with migrate_updates=True at connect
  • Wrap service/TUI entrypoints in in_db_session; use lambda where() predicates throughout
  • Add upstream regression scripts for the 0.12.2 fixes (operator predicates, unnamed session, cross-context teardown)

Test plan

  • uv run pytest -W error::DeprecationWarning (318 passed)
  • just check
  • Smoke ttd TUI + a few CLI commands against real ledger after merge

Made with Cursor

0x054 and others added 3 commits June 24, 2026 11:19
Adopt session-scoped queries, migrate_updates at connect, and lambda
where() predicates so TTD is ready for v0.14 and drops the hand-rolled
column-add shim.

Co-authored-by: Cursor <cursoragent@cursor.com>
… revamp (#14)

This branch grew well beyond its original "billable expenses" scope. It
now bundles four related pieces of invoicing/expense work. Base is
`chore/ferro-0-12-migration` (the feature depends on the ferro-orm 0.12
migration).

## 1. Billable expenses (client chargebacks)
Record purchased items against a project and bill them back as
**untaxed, pass-through** line items on invoices, with optional
receipts.
- New `Expense` / `ExpenseReceipt` / `InvoiceExpenseLine` models
(plain-id FK convention; receipts stored base64 in a side table because
raw bytes can't round-trip through the ORM — ferro-orm#160).
- `ttd expense` CLI: `add` / `list` / `edit` / `rm` + a `receipt` group;
history-based recall.
- Invoicing lifecycle: expenses lock on invoice, release on void, and
participate in refresh. Tax applies only to the time subtotal; `total =
subtotal + tax + expenses_subtotal`. Invoices with no expenses render
byte-identically to before.
- Rendering: PDF/markdown expense section; **opt-in receipt pages**
merged into the PDF via `pypdf`; invoice format choice (default PDF)
with markdown blocked when receipts are included.
- JSON backup (envelope v2) round-trips expenses + receipts (incl.
non-UTF-8 binary); v1 backups still import.

## 2. TUI "log" page (re-scoped timesheet)
The underused timesheet screen became a month-scoped **log** page (nav
2) with stacked **time** and **expense** sections: `[`/`]` cycle months,
`tab` switches section, `e`/`x` edit/delete the focused row, `l` adds
(time or expense via a chooser). Expenses also show in the new-invoice
draft preview.

## 3. Flexible invoice periods
Richer `--period` parsing (invoice CLI + TUI): `this week`/`last week`,
rolling `last N days/weeks/months`, and month-name ranges (`june 16 to
june 30`, `jun`, `june`, `june 16 - 30`) with a
closest-year-never-future rule and cross-year wrap. Separately, an
invoice's recorded period now **derives from the billed items** (min–max
of line dates), not the requested window — so a mid-month re-invoice
reads the true span. Refresh re-derives.

## 4. TUI invoice render format modal
`e` on the invoices screen now opens a **render chooser** (checkboxes:
Include receipts / PDF / Markdown): receipts embed into the PDF, the
receipts option is disabled unless the invoice has receipts, and
enabling it locks out markdown. A shared `load_invoice_receipts` helper
backs both the CLI and TUI.

## Testing
Full suite green throughout: **397 passing, 84.59% coverage, `ty` +
`ruff` clean.** Built brainstorm → spec → plan → subagent implementation
with per-task review and an opus whole-branch review per feature.
Specs/plans under `docs/superpowers/`.

## Known follow-ups (not in this PR)
- TUI: attach a receipt when adding an expense (drag-and-drop path into
the form) — designed-but-deferred to a future session.
- `invoice_has_receipts` / some services use `.all()`+filter scans
(codebase convention; tied to the relationship migration in #13).
- Multi-currency expense-total footer on the log page (edge case).

## Related issues
- ferro-orm#160 (raw `bytes` can't be saved via the ORM — drove the
base64 receipt design).
- #13 (migrate models to Ferro relationships + cascades).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The global `t`=theme binding shadowed the invoices screen's `t`=mark-sent.
Move the theme picker to ctrl+t (matching the palette convention), freeing
`t` for mark-sent. Updated theme-picker tests to ctrl+t and added a
regression test that `t` on the invoices screen marks the invoice sent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant