0e4033aa3f7d7c86b5d56cda2f980d6804dc87fb
A payment recorded by the lab itself is self-evident (the lab knows it
got paid). One recorded by the clinic is just a claim until the lab
agrees. Added a status field to enforce that distinction so labs can
approve payments per-clinic instead of trusting whatever the clinic
typed in.
DB
- payments.status enum (pending | confirmed | rejected, default
confirmed). Existing rows keep the default and continue to be
counted in balances.
Server
- recordPaymentAction now stamps status='pending' when the caller is a
clinic and 'confirmed' when the caller is a lab. A clinic submission
pings the lab via createNotification so it surfaces on the
notifications bell as well as on /finance.
- confirmPaymentAction (lab only): flips a pending row to confirmed
after verifying the lab is the counterpart. Notifies the clinic on
success.
- rejectPaymentAction (lab only): flips to rejected, notifies the
clinic. Rejected rows stay visible for audit but never count toward
the balance.
Queries
- listIncomingPayments(tenantId) — payments where this tenant is the
counterpart (the other side recorded them). Paired with listPayments
we now see the same physical payment from either ledger.
- computeBalancesByCounterpart upgraded to handle both shapes via an
inflowFor() helper that normalises 'who got the money'. Only
confirmed rows reduce the open balance.
- filterPendingForConfirmation() returns the lab-side approval queue,
sorted newest-first.
UI
- /finance loads own + incoming payments, dedupes by $id, then feeds
the merged list to balance/pending helpers.
- New PendingPaymentsCard sits above the balances table on the lab
side. Per-row: clinic name, amount, date, method, note, plus inline
Onayla / Reddet buttons. Empty state hides the whole card.
- Confirm + reject use the same router.refresh pattern as the rest of
the action panels so the queue and the balances both update without
a manual reload.
Description
No description provided
Languages
TypeScript
99.2%
CSS
0.7%