0054 — Remittance line amounts cap per-invoice allocation (short-pay support)
Status: accepted (owner sign-off 2026-08-20) · Date: 2026-08-20
Context
allocateCheck spends a check down the targeting order greedily, paying each invoice's full
outstanding balance until the check is exhausted. Remittance line amounts influenced matching
(which invoice a line names) but never application (how much lands on it). That was invisible
while payers paid exact balances. Check #5085 (Westchester, 2026-08-20) broke the assumption: the
stub put $2,440.00 on INV-0973 against a $2,552.70 balance — a deliberate short-pay — and greedy
saturation applied the full $2,552.70, taking $112.70 from INV-0977, which the payer had paid in
full. Same total money, wrong invoices: AR chasing targets an invoice the facility's books show as
paid, and the short-pay dispute disappears from the invoice it belongs to.
Decision
When a matched remittance line carries a positive amount, that amount caps the application to
its invoice: applied = min(check remainder, outstanding, line amount). Everything else is
unchanged — uncapped (fallback) invoices still saturate oldest-first per decisions.md D1, a capped
invoice is partial, never "paid" (paid compares against the true outstanding), and unapplied
remainder behaves as before (spread for single-facility payers, held for review for
management-company payers). Zero/illegible line amounts cap nothing. The cap definition lives once
in src/lib/reconcile (lineAmountCaps) and is used by both the on-screen preview and the server
confirm, which refuses on any mismatch.
Consequences
- A short-paid invoice keeps its unpaid remainder on the invoice the payer disputed, matching the facility's own books; statements and AR chasing point at the right place.
- A capped invoice is never revisited by the same check's remainder — the payer said how much goes there. If the payer under-states every line, more money flows to the fallback (visible in the preview before confirm).
- Confirms in flight across the deploy are refused as stale and need a page refresh (by design of the reviewed-allocation binding).