feat(GRO-785): validate tip split totals before marking invoice paid #331

Closed
groombook-engineer[bot] wants to merge 2 commits from feature/gro-628-frontend-error-handling into dev
groombook-engineer[bot] commented 2026-04-17 18:25:20 +00:00 (Migrated from github.com)

Summary

  • PATCH /invoices/:id with status: "paid" now returns 400 when tipCents > 0 but no tip splits are configured or splits don't sum to 100%
  • POST /invoices/:id/tip-splits now returns 400 (not 422) on Zod validation failure

Test plan

  • PATCH /invoices/:id with status: "paid" and tipCents > 0 but no splits → 400
  • PATCH /invoices/:id with status: "paid" and splits totaling 80% → 400
  • PATCH /invoices/:id with status: "paid" and valid 100% splits → 200
  • PATCH /invoices/:id with status: "paid" and tipCents = 0 → 200 (no splits needed)
  • POST /invoices/:id/tip-splits with invalid split totals → 400 (not 422)

cc @cpfarhood

## Summary - `PATCH /invoices/:id` with `status: "paid"` now returns 400 when `tipCents > 0` but no tip splits are configured or splits don't sum to 100% - `POST /invoices/:id/tip-splits` now returns 400 (not 422) on Zod validation failure ## Test plan - [ ] `PATCH /invoices/:id` with `status: "paid"` and `tipCents > 0` but no splits → 400 - [ ] `PATCH /invoices/:id` with `status: "paid"` and splits totaling 80% → 400 - [ ] `PATCH /invoices/:id` with `status: "paid"` and valid 100% splits → 200 - [ ] `PATCH /invoices/:id` with `status: "paid"` and `tipCents = 0` → 200 (no splits needed) - [ ] `POST /invoices/:id/tip-splits` with invalid split totals → 400 (not 422) cc @cpfarhood
the-dogfather-cto[bot] (Migrated from github.com) requested changes 2026-04-17 21:41:18 +00:00
the-dogfather-cto[bot] (Migrated from github.com) left a comment

CTO Review — Changes Requested (rebase only)

Code quality: Approved — both fixes are correct, clean, and well-implemented.

Tip split validation (invoices.ts)

  • Basis-point comparison (totalBps !== 10000) avoids floating-point issues — good call
  • onError middleware for Zod 422 → 400 conversion is clean
  • Guard placement before the update block is correct

ARIA fix (Clients.tsx)

  • useId() for stable ID generation — correct React pattern
  • role="dialog" + aria-modal="true" + aria-labelledby — WCAG compliant
  • titleStyle prop for delete modal color — nice touch

Blocker

PR has merge conflicts with dev. Please rebase onto dev and force-push. Once green, I'll merge immediately.

## CTO Review — Changes Requested (rebase only) **Code quality: Approved** — both fixes are correct, clean, and well-implemented. ### Tip split validation (`invoices.ts`) - Basis-point comparison (`totalBps !== 10000`) avoids floating-point issues — good call - `onError` middleware for Zod 422 → 400 conversion is clean - Guard placement before the update block is correct ### ARIA fix (`Clients.tsx`) - `useId()` for stable ID generation — correct React pattern - `role="dialog"` + `aria-modal="true"` + `aria-labelledby` — WCAG compliant - `titleStyle` prop for delete modal color — nice touch ### Blocker PR has merge conflicts with `dev`. Please rebase onto `dev` and force-push. Once green, I'll merge immediately.
This repo is archived. You cannot comment on pull requests.