mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 17:38:12 +00:00
feat: ( payment ) specific CBE query descriptions + Payment_Reason
This commit is contained in:
@@ -55,6 +55,27 @@ const OPEN_STATUSES: Freight.InvoiceStatus[] = [
|
||||
Freight.InvoiceStatus.Overdue,
|
||||
];
|
||||
|
||||
/**
|
||||
* Why a non-open invoice can no longer be paid, in the vocabulary the payment service's CBE
|
||||
* bill-query mapper understands. Kept specific: CBE reads this back to the payer at the counter,
|
||||
* so "cancelled" must not stand in for "already paid" or "refunded".
|
||||
*/
|
||||
function closedInvoiceReason(status: Freight.InvoiceStatus): string {
|
||||
switch (status) {
|
||||
case Freight.InvoiceStatus.Paid:
|
||||
return "ALREADY_PAID";
|
||||
case Freight.InvoiceStatus.Refunded:
|
||||
return "REFUNDED";
|
||||
case Freight.InvoiceStatus.Cancelled:
|
||||
return "CANCELLED";
|
||||
case Freight.InvoiceStatus.Expired:
|
||||
return "EXPIRED";
|
||||
// Draft — issued to nobody yet, so there is nothing honest to say beyond "not payable".
|
||||
default:
|
||||
return "NOT_PAYABLE";
|
||||
}
|
||||
}
|
||||
|
||||
/** A single line to bill on a generated invoice. */
|
||||
export interface InvoiceLineInput {
|
||||
chargeType: string;
|
||||
@@ -1097,6 +1118,7 @@ export class BillingService {
|
||||
currentAmountMinor?: number | null;
|
||||
currency?: string | null;
|
||||
reason?: string | null;
|
||||
paymentReason?: string | null;
|
||||
}> {
|
||||
const repo = this.dataSource.getRepository(Invoice);
|
||||
const open = await repo.findOne({
|
||||
@@ -1113,7 +1135,12 @@ export class BillingService {
|
||||
payerName: open.company?.name ?? null,
|
||||
currentAmountMinor: balance,
|
||||
currency: open.currency,
|
||||
reason: expired ? "EXPIRED" : balance > 0 ? null : "ALREADY_PAID",
|
||||
// CBE shows this beside the amount on the confirmation screen — the invoice number
|
||||
// the payer is holding, not our internal reference.
|
||||
paymentReason: `Freight invoice ${open.invoiceNumber}`,
|
||||
// Settled-in-full wins over past-due: an invoice with nothing left to pay is paid, not
|
||||
// expired, and that is what the payer at the CBE counter must be told.
|
||||
reason: balance > 0 ? (expired ? "EXPIRED" : null) : "ALREADY_PAID",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1122,15 +1149,24 @@ export class BillingService {
|
||||
relations: { company: true },
|
||||
order: { createdAt: "DESC" },
|
||||
});
|
||||
// A bill reference whose invoice no longer exists at all — a data problem, not a
|
||||
// cancellation the payer did anything to cause.
|
||||
if (!latest) {
|
||||
return {
|
||||
stillPayable: false,
|
||||
payerName: null,
|
||||
currentAmountMinor: null,
|
||||
currency: null,
|
||||
reason: "NOT_FOUND",
|
||||
};
|
||||
}
|
||||
return {
|
||||
stillPayable: false,
|
||||
payerName: latest?.company?.name ?? null,
|
||||
currentAmountMinor: latest ? Math.round(Number(latest.totalAmount)) : null,
|
||||
currency: latest?.currency ?? null,
|
||||
reason:
|
||||
latest?.status === Freight.InvoiceStatus.Paid
|
||||
? "ALREADY_PAID"
|
||||
: "CANCELLED",
|
||||
payerName: latest.company?.name ?? null,
|
||||
currentAmountMinor: Math.round(Number(latest.totalAmount)),
|
||||
currency: latest.currency,
|
||||
paymentReason: `Freight invoice ${latest.invoiceNumber}`,
|
||||
reason: closedInvoiceReason(latest.status),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user