feat(billing): replace Record Payment UUID field with an invoice picker #218

Merged
David merged 2 commits from feat/MAPPS-191-record-payment-invoice-picker into main 2026-06-17 12:23:34 +02:00
Owner

The Record Payment modal collected the target invoice via a free-text "Invoice ID (UUID, optional)" input, which no user can be expected to know or paste, and on save silently coerced a non-empty but unparseable UUID to Null (an unapplied payment), detaching a money record from its invoice on a single-character typo with no warning.

Replace the input with a dependent Select populated from GET /invoices?company_id=<id>. The list re-fetches whenever the selected company changes (reading the company_id signal inside the resource subscribes it), each option shows the human invoice number plus amount and status keyed by the invoice UUID, and a leading explicit "(Unapplied payment)" blank choice stays selectable. A company with no invoices yields a valid unapplied-only control rather than a broken one.

Because the picker can only ever yield the explicit blank or a valid UUID, the silent-coercion path is gone: an empty value is a deliberate unapplied payment while a non-empty unparseable value is now a hard inline error. Switching companies also clears any previously picked invoice so a stale cross-company UUID cannot be submitted.

#MAPPS-191

The Record Payment modal collected the target invoice via a free-text "Invoice ID (UUID, optional)" input, which no user can be expected to know or paste, and on save silently coerced a non-empty but unparseable UUID to Null (an unapplied payment), detaching a money record from its invoice on a single-character typo with no warning. Replace the input with a dependent Select populated from `GET /invoices?company_id=<id>`. The list re-fetches whenever the selected company changes (reading the `company_id` signal inside the resource subscribes it), each option shows the human invoice number plus amount and status keyed by the invoice UUID, and a leading explicit "(Unapplied payment)" blank choice stays selectable. A company with no invoices yields a valid unapplied-only control rather than a broken one. Because the picker can only ever yield the explicit blank or a valid UUID, the silent-coercion path is gone: an empty value is a deliberate unapplied payment while a non-empty unparseable value is now a hard inline error. Switching companies also clears any previously picked invoice so a stale cross-company UUID cannot be submitted. #MAPPS-191
feat(billing): replace Record Payment UUID field with an invoice picker
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 58s
6ad02f5fca
The Record Payment modal collected the target invoice via a free-text "Invoice ID (UUID, optional)" input, which no user can be expected to know or paste, and on save silently coerced a non-empty but unparseable UUID to Null (an unapplied payment), detaching a money record from its invoice on a single-character typo with no warning.

Replace the input with a dependent Select populated from `GET /invoices?company_id=<id>`. The list re-fetches whenever the selected company changes (reading the `company_id` signal inside the resource subscribes it), each option shows the human invoice number plus amount and status keyed by the invoice UUID, and a leading explicit "(Unapplied payment)" blank choice stays selectable. A company with no invoices yields a valid unapplied-only control rather than a broken one.

Because the picker can only ever yield the explicit blank or a valid UUID, the silent-coercion path is gone: an empty value is a deliberate unapplied payment while a non-empty unparseable value is now a hard inline error. Switching companies also clears any previously picked invoice so a stale cross-company UUID cannot be submitted.

#MAPPS-191
Merge branch 'main' into feat/MAPPS-191-record-payment-invoice-picker
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / clippy + fmt + tests (pull_request) Successful in 3m12s
0f5392f4f4
Resolved the conflict in src/pages/billing.rs between this branch's Record Payment invoice picker (MAPPS-191) and the field-level validation that landed on main (MAPPS-215). Kept the dependent Select picker as the invoice field, kept main's per-field validation (Decimal amount checks, the `ok` accumulator, and the invoice-existence confirmation before recording), and wired main's `invoice_err` signal into the Select so a rejected value surfaces beneath the field. Dropped this branch's now-superseded save-side invoice handling, since main's `invoice_uuid` block already removes the silent bad-UUID -> unapplied coercion.

#MAPPS-191

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David merged commit 3cce25fbf1 into main 2026-06-17 12:23:34 +02:00
David deleted branch feat/MAPPS-191-record-payment-invoice-picker 2026-06-17 12:23:34 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!218
No description provided.