fix issue

This commit is contained in:
Marshal
2026-07-16 00:33:31 +00:00
parent 234a74e812
commit 41fe04652f
51 changed files with 1895 additions and 206 deletions

View File

@@ -119,6 +119,26 @@ export class BookingInvoiceService {
return this.billing.updateStatus(invoiceId, status, manager);
}
/**
* Expire the booking's currently-open prepaid invoice when the booking is
* cancelled or rejected — the counterpart to the pay-window-expiry path
* (which also calls {@link BillingService.expirePayable}). Stops a terminated
* booking from leaving a payable invoice open. No-op when the booking has no
* open invoice (never invoiced, already paid/cancelled/expired). Pass a
* caller `manager` to enlist in its transaction.
*/
expireOpenInvoices(
bookingId: string,
manager?: EntityManager,
): Promise<Invoice | null> {
return this.billing.expirePayable(
Freight.InvoiceSource.Booking,
bookingId,
"PREPAID",
manager,
);
}
/**
* Advance a booking once its prepaid invoice settles — the domain side-effect
* of payment, relocated out of the payment service: the booking becomes PAID
@@ -138,7 +158,32 @@ export class BookingInvoiceService {
);
return;
}
// if (booking.paymentStatus === "PAID") return;
// Idempotency + state-machine guard (restored). The prepaid-invoice paid
// event can be delivered more than once (retries / re-emit), and a booking
// may have moved on or been terminated between invoicing and settlement.
// Only advance one that is still awaiting payment: no-op when already PAID,
// and refuse to advance a booking in a terminal/advanced status
// (CANCELLED/REJECTED/EXPIRED or already past the payment gate) so we never
// rewrite its status or re-run allocation.
if (booking.paymentStatus === "PAID" || booking.status === "PAID") {
return;
}
const TERMINAL_OR_ADVANCED_STATUSES: string[] = [
"CANCELLED",
"REJECTED",
"EXPIRED",
"IN_TRANSIT",
"ARRIVED",
"COMPLETED",
"CONTRACT_CLOSED",
];
if (TERMINAL_OR_ADVANCED_STATUSES.includes(booking.status)) {
this.logger.warn(
`Skipping advance of booking ${bookingId} on payment: status ${booking.status} is terminal/advanced.`,
);
return;
}
await this.dataSource.transaction(async (mg) => {
await mg.update(