feat(billing): show the booking PNR on an invoice

The PNR is the CBE_BILL reference the customer actually pays against, but it
is stamped onto the booking at payment-initiation time — it is a column on
neither the invoice nor the payment. Both surfaces read it back by source id,
the same lookup the sealed invoice PDF already did, so screen, export and
document now agree.

The export's join casts bk.id::text rather than i.source_id::uuid: source_id
is a bare varchar pointer that is not always a UUID (EIMS self-test rows
carry a slug), and casting that direction throws on those rows.
This commit is contained in:
Nathnael
2026-08-24 07:02:46 +00:00
parent 018505d6fa
commit 3da00e1f06
2 changed files with 18 additions and 0 deletions

View File

@@ -145,6 +145,14 @@ function RecipientCard({ invoice }: { invoice: Invoice }) {
function PaymentCard({ invoice }: { invoice: Invoice }) {
const method = invoicePaymentMethod(invoice);
const lastEntry = invoice.payments?.at(-1);
// The PNR is the CBE_BILL reference the customer pays against. It is stamped
// onto the BOOKING at payment-initiation time, not onto the invoice or the
// payment, so it has to be read back from there — same lookup the sealed PDF
// does. Shares react-query's cache with `SourceCard`, so this costs no
// second request.
const { data: booking } = useBookingDetail(
invoice.source === "booking" ? invoice.sourceId : undefined,
);
if (!method && !lastEntry) return null;
// The gateway's own reference first; `merchantOrderId` is our order id, which
@@ -163,6 +171,7 @@ function PaymentCard({ invoice }: { invoice: Invoice }) {
name={method ? paymentMethodLabel(method) : "Settled"}
rows={[
{ label: "Transaction ref", value: transactionRef },
{ label: "PNR", value: booking?.pnrCode ?? undefined },
{ label: "Provider status", value: invoice.payment?.status },
{
label: "Paid",