mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
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:
@@ -2,6 +2,7 @@ import { FREIGHT_PERMS } from '../../../seed/freight-permissions.registry';
|
||||
import { Invoice } from '../../billing/entities/invoice.entity';
|
||||
import { invoicePaymentMethodExpr } from '../../billing/invoice-settlement.util';
|
||||
import { PaymentEntity } from '../../payment/entities/payment.entity';
|
||||
import { Booking } from '../../bookings/entities/booking.entity';
|
||||
import { Company } from '../../companies/entities/company.entity';
|
||||
import { CompanyProfile } from '../../companies/entities/company-profile.entity';
|
||||
import { ShippingLineCompany } from '../../shipping-lines/entities/shipping-line-company.entity';
|
||||
@@ -34,6 +35,11 @@ export const invoicesDataset: ExportDataset = {
|
||||
// The gateway payment behind the invoice — provider method and its
|
||||
// transaction reference. Always joined: `scope()` filters on it.
|
||||
{ alias: 'p', entity: PaymentEntity, on: 'p.id = i.payment_id' },
|
||||
// Booking behind the invoice, for the PNR alone. `i.source_id` is a bare
|
||||
// varchar pointer that is not always a UUID (EIMS self-test rows carry a
|
||||
// slug), so the cast goes on `bk.id`, never on `source_id` — casting the
|
||||
// other way throws on those rows.
|
||||
{ alias: 'bk', entity: Booking, on: "bk.id::text = i.source_id AND i.source = 'booking'" },
|
||||
],
|
||||
alwaysJoin: ['c', 'p'],
|
||||
|
||||
@@ -76,6 +82,9 @@ export const invoicesDataset: ExportDataset = {
|
||||
{ key: 'paidAt', label: 'Paid at', type: 'datetime', group: 'payment', select: `to_char(i.paid_at, 'YYYY-MM-DD HH24:MI')` },
|
||||
{ key: 'paymentMethod', label: 'Payment method', type: 'string', group: 'payment', default: true, requires: ['p'], select: PAYMENT_METHOD, sortExpr: PAYMENT_METHOD },
|
||||
{ key: 'transactionRef', label: 'Transaction ref', type: 'string', group: 'payment', requires: ['p'], select: 'p.transaction_id' },
|
||||
// The CBE_BILL reference the customer pays against — stamped onto the
|
||||
// booking at payment-initiation time, not held on the invoice or payment.
|
||||
{ key: 'pnrCode', label: 'PNR', type: 'string', group: 'payment', requires: ['bk'], select: 'bk.pnr_code' },
|
||||
{ key: 'paymentStatus', label: 'Payment status', type: 'string', group: 'payment', requires: ['p'], select: 'p.status::text' },
|
||||
{
|
||||
key: 'daysOverdue', label: 'Days overdue', type: 'number', group: 'payment',
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user