chore: upgrade ferro-orm to 0.14 - #15
Merged
Merged
Conversation
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>
Bump ferro-orm from 0.12.x to 0.14.x. Pin all datetime fields with db_type="timestamp" so 0.14's timestamptz default does not alter existing SQLite storage. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace relative "yesterday" log specs so invoice --month 2026-06 tests stay stable regardless of the runner's current date. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ferro-ormfrom0.12.xto0.14.xdatetimemodel fields withdb_type="timestamp"so Ferro 0.14'stimestamptzdefault does not alter existing SQLite storageTest plan
just checkpytest tests/test_storage tests/test_invoicing tests/test_interchange tests/test_taxesuv run pytest(11 CLI invoice/tax tests fail on date-sensitive_seedusingyesterdayvs--month 2026-06— pre-existing, unrelated to Ferro)Made with Cursor