Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
72e8c81
docs: billable expenses (client chargebacks) design
0x054 Jun 30, 2026
0a4503f
docs: billable expenses implementation plan
0x054 Jun 30, 2026
0d91bf8
feat: add Expense, ExpenseReceipt, InvoiceExpenseLine models
0x054 Jun 30, 2026
0a9e2e7
feat: expense CRUD service with history recall
0x054 Jun 30, 2026
b687ae8
fix: guard expense list against dangling client references
0x054 Jun 30, 2026
17110b0
feat: expense receipt storage (base64, size-guarded)
0x054 Jun 30, 2026
bf65bcb
feat: ttd expense CLI (add/list/edit/rm + receipt group)
0x054 Jun 30, 2026
eafe240
feat: bill expenses on invoices (untaxed totals, lock on persist)
0x054 Jun 30, 2026
64a0357
feat: release and refresh expenses through invoice lifecycle
0x054 Jun 30, 2026
a466ccc
test: strengthen expense refresh assertions
0x054 Jun 30, 2026
c9bc783
feat: render expense section on PDF and markdown invoices
0x054 Jun 30, 2026
7aa8b67
feat: append expense receipts to invoice PDFs (opt-in)
0x054 Jun 30, 2026
96cd243
feat: invoice format choice (default PDF) with receipt-aware markdown…
0x054 Jun 30, 2026
a69a7ba
feat: include expenses and receipts in JSON backup (envelope v2)
0x054 Jun 30, 2026
9201899
test: cover expense backup restore branches (restore coverage gate)
0x054 Jun 30, 2026
bb110ae
fix: do not replace receipts for skipped expenses on import
0x054 Jun 30, 2026
8e11ad5
feat: TUI invoice expenses view and quick-add
0x054 Jun 30, 2026
0a4d8a3
fix: address final-review findings (export filters, backup fidelity, …
0x054 Jun 30, 2026
4f7b709
feat: TUI log chooser — log time or an expense from l
0x054 Jun 30, 2026
8feda8a
fix: surface all errors in TUI expense form; clarify date test names
0x054 Jun 30, 2026
95622ef
feat: show expenses in TUI new-invoice draft preview
0x054 Jun 30, 2026
23cd25e
docs: TUI log page design (re-scope timesheet)
0x054 Jun 30, 2026
843a765
docs: TUI log page implementation plan
0x054 Jun 30, 2026
f33ac40
feat: re-scope timesheet into month-only log screen
0x054 Jun 30, 2026
2a19ed2
chore: update stale timesheet references in theme picker and footer t…
0x054 Jun 30, 2026
1d945ea
feat: show expenses section on the log screen
0x054 Jun 30, 2026
28ff116
feat: focus switching and expense edit/delete on the log screen
0x054 Jun 30, 2026
69905d4
fix: refuse invoiced expense before confirm on log delete
0x054 Jun 30, 2026
2b18160
test: tighten log-page tests; note single-currency total assumption
0x054 Jun 30, 2026
8a0abd8
docs: flexible invoice periods design
0x054 Jul 1, 2026
60c9d07
docs: flexible invoice periods implementation plan
0x054 Jul 1, 2026
a78bfe9
feat: relative period specs (this/last week, last N days/weeks/months)
0x054 Jul 1, 2026
f67a981
test: make invoice/log tests date-deterministic at month boundaries
0x054 Jul 1, 2026
46ed137
feat: month-name period ranges with closest-year inference
0x054 Jul 1, 2026
1b547b1
feat: record invoice period from billed items, not the requested window
0x054 Jul 1, 2026
3ba3c96
refactor: clearer period count error and derived-period comments
0x054 Jul 1, 2026
17f0c63
docs: TUI invoice render format modal design
0x054 Jul 1, 2026
0ab2a84
docs: TUI invoice render format modal implementation plan
0x054 Jul 1, 2026
3309124
refactor: shared load_invoice_receipts helper for CLI and TUI
0x054 Jul 1, 2026
083eb91
feat: TUI invoice render format modal with receipts and md gating
0x054 Jul 1, 2026
52a0e05
test: cover render-modal validation; restore receipt count in notify
0x054 Jul 1, 2026
5a11192
feat: render modal uses checkboxes, receipts first, grouped formats
0x054 Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .superpowers/sdd/final-review-fixes-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Final Review Fixes Report

## Finding 1 — `--invoiced` filter now applied to expenses on export

**File:** `src/ttd/services/interchange_svc.py`

Moved `expense_views = await list_expenses(...)` before the `used_clients`/`used_projects` sets are built. Added a filter immediately after fetching: `if invoiced is not None: expense_views = [v for v in expense_views if (v.expense.invoice_id is not None) == invoiced]`. The existing `receipts_meta` is derived from `expense_ids` which is now computed from the filtered `expense_views`, so receipts for filtered-out expenses are correctly excluded.

**Test added:** `test_export_invoiced_filter_applies_to_expenses` in `tests/test_interchange/test_expense_backup.py` — creates one uninvoiced expense and one with a fake `invoice_id`, then asserts `invoiced=True` returns only the invoiced one, `invoiced=False` only the free one, and `invoiced=None` returns both.

---

## Finding 2 — Expense-only clients now included in JSON backup metadata

**File:** `src/ttd/services/interchange_svc.py`

After the expense filter, added:
```python
used_clients |= {v.client.slug for v in expense_views}
used_projects |= {(v.client.slug, v.project.slug) for v in expense_views}
```
before building `clients_meta`/`projects_meta`. Also deduplicated the `Client.all()` call (was called twice; now uses `all_clients` local variable).

**Test added:** `test_export_includes_expense_only_client_in_meta` in `tests/test_interchange/test_expense_backup.py` — creates a client with `currency="EUR"` that has an expense but no entries, then asserts the client and project appear in `meta["clients"]` and `meta["projects"]` with correct name and currency.

---

## Finding 3 — Refresh diff now prints Expenses line when expense subtotal changes

**File:** `src/ttd/cli/invoices.py`, function `_print_refresh_diff`

Added a block inside the `if preview.totals_changed:` branch:
```python
if preview.before_expenses_subtotal != preview.after_expenses_subtotal:
console.print(
f"Expenses: {format_money(preview.before_expenses_subtotal, currency)} → "
f"[bold]{format_money(preview.after_expenses_subtotal, currency)}[/bold]"
)
```
Placed between the Subtotal and Tax lines, matching the existing arrow style.

**Test added:** `test_print_refresh_diff_shows_expenses_line_when_expense_subtotal_changes` in `tests/test_cli/test_invoice_cli.py` — builds a `RefreshPreview` with `before_expenses_subtotal=100` and `after_expenses_subtotal=50`, calls `_print_refresh_diff`, and asserts "Expenses" appears in the captured stdout.

---

## Finding 4 — Hardcoded 'USD' replaced in expense CLI messages

**File:** `src/ttd/cli/expenses.py`

- Moved `get_settings` import to module level (was only inside `add`).
- `add` success message: `format_money(expense.amount, get_settings().business.currency)` (extracted to `currency` local for line-length).
- `list` footer total: uses `rows[0].client.currency` when rows exist; falls back to `"USD"` only in the impossible case that rows is empty (the function returns early if no rows).
- `rm` success message: `format_money(expense.amount, get_settings().business.currency)` (extracted to `currency` local).

No test added (behavior is cosmetic and covered by existing CLI integration tests).

---

## Final Results

- **pytest:** 361 passed, 0 failed
- **Coverage:** 84.60% (threshold: 84%)
- **ty check:** All checks passed
- **ruff check:** All checks passed
4 changes: 3 additions & 1 deletion docs/pages/reference/cli/invoice.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ Invoice a client's uninvoiced billable work (defaults to last month).

* `--client`: Client slug
* `--month`: YYYY-MM
* `--period`: Period spec: 'last month', 'this month', YYYY-MM, or YYYY-MM-DD to YYYY-MM-DD
* `--period`: Period spec: 'last month', 'this week', 'last two weeks', 'june 16 to june 30', YYYY-MM, or YYYY-MM-DD to YYYY-MM-DD
* `--from`:
* `--to`:
* `--number`: Override the number
* `--pdf, --no-pdf`: Render a PDF *\[default: False\]*
* `--md, --no-md`: Render Markdown *\[default: False\]*
* `--receipts, --no-receipts`: Append expense receipts to the PDF *\[default: False\]*
* `--out`: Output directory
* `--dry-run, --no-dry-run`: Preview, change nothing *\[default: False\]*
* `--interactive, --no-interactive, -i`: Fill remaining fields via a form *\[default: False\]*
Expand Down Expand Up @@ -75,6 +76,7 @@ ttd invoice render [OPTIONS] NUMBER
* `NUMBER, --number`: **\[required\]**
* `--pdf, --no-pdf`: *\[default: False\]*
* `--md, --no-md`: *\[default: False\]*
* `--receipts, --no-receipts`: Append expense receipts to the PDF *\[default: False\]*
* `--out`:

## invoice refresh
Expand Down
Loading
Loading