fix: pagination count 500s, order_by, and billing state (MAPPS-135) #122
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/mapps-135-pagination-billing-correctness"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cluster of server-side correctness defects in pagination count-query construction and billing state handling. All present on main; the count-query and order_by bugs were touched by later commits but never actually fixed.
count-query param indices: the list queries bind $1=tenant_id, $2=limit, $3=offset so filter placeholders start at $4, but the count queries reuse the same WHERE clause while binding tenant_id at $1 and filters from $2. Any filtered list 500s with a Postgres parameter error. Build a separate count WHERE clause off a parallel counter starting at $2 and bind it in the same order. Fixed in
list_time_entries,list_invoices, andlist_payments.order_by double-direction:
order_by()didformat!("{} {}", field, direction), so a default that already embeds a direction produced"... DESC DESC"(invalid SQL on the no-?sort=path). It now appends a direction only to an explicit allow-listed sort or to a bare single-column default; a compound or already-directional default is emitted verbatim. The single-column billing defaults are stripped to bareinvoice_date/payment_date; the time-entry default stays the compounddate DESC, start_time DESC(it cannot be a bare field) and is now emitted verbatim without double-appending.create_payment frozen guard: recording a payment now fetches the invoice status first and rejects (Conflict) a payment against a void / written_off invoice, which previously un-voided it via the status overwrite.
is_frozen()also covers the normal sent/paid/partially_paid payment targets, so the gate is the terminal cancelled subset.unknown enum fallback: the row-to-response conversions silently mapped unknown DB enum strings to Draft / Stripe / Other / Service, which could re-enable edits on a frozen invoice. The three conversions become
TryFromreturning a Database error on an unknown value, and the gateway-provider and create_payment status parses do the same.payment DELETE feedback: the payments table swallowed a failed delete via
.is_ok(). It now matches the result and surfaces an error line, mirroring the tax-rate delete.lookup_tax_rate guard: added the missing
RequireFinanceextractor so tax-rate lookup is finance-only like the other tax-rate routes.compute_minutes overnight: an entry whose end_time precedes start_time now wraps by a full day (crosses midnight) instead of being rejected as a bad request.
#MAPPS-135
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Cluster of server-side correctness defects in pagination count-query construction and billing state handling. All present on main; the count-query and order_by bugs were touched by later commits but never actually fixed. count-query param indices: the list queries bind $1=tenant_id, $2=limit, $3=offset so filter placeholders start at $4, but the count queries reuse the same WHERE clause while binding tenant_id at $1 and filters from $2. Any filtered list 500s with a Postgres parameter error. Build a separate count WHERE clause off a parallel counter starting at $2 and bind it in the same order. Fixed in `list_time_entries`, `list_invoices`, and `list_payments`. order_by double-direction: `order_by()` did `format!("{} {}", field, direction)`, so a default that already embeds a direction produced `"... DESC DESC"` (invalid SQL on the no-`?sort=` path). It now appends a direction only to an explicit allow-listed sort or to a bare single-column default; a compound or already-directional default is emitted verbatim. The single-column billing defaults are stripped to bare `invoice_date` / `payment_date`; the time-entry default stays the compound `date DESC, start_time DESC` (it cannot be a bare field) and is now emitted verbatim without double-appending. create_payment frozen guard: recording a payment now fetches the invoice status first and rejects (Conflict) a payment against a void / written_off invoice, which previously un-voided it via the status overwrite. `is_frozen()` also covers the normal sent/paid/partially_paid payment targets, so the gate is the terminal cancelled subset. unknown enum fallback: the row-to-response conversions silently mapped unknown DB enum strings to Draft / Stripe / Other / Service, which could re-enable edits on a frozen invoice. The three conversions become `TryFrom` returning a Database error on an unknown value, and the gateway-provider and create_payment status parses do the same. payment DELETE feedback: the payments table swallowed a failed delete via `.is_ok()`. It now matches the result and surfaces an error line, mirroring the tax-rate delete. lookup_tax_rate guard: added the missing `RequireFinance` extractor so tax-rate lookup is finance-only like the other tax-rate routes. compute_minutes overnight: an entry whose end_time precedes start_time now wraps by a full day (crosses midnight) instead of being rejected as a bad request. #MAPPS-135 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>