mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
feat: enhance booking cancellation logic for consolidated pairs with one paid side
This commit is contained in:
@@ -3991,9 +3991,10 @@ export class BookingBatchService implements OnModuleInit {
|
||||
* taken, so it boards, even when the webhook arrived after the deadline or the
|
||||
* settle read a stale row. It allocates onto the train it was selected for; if
|
||||
* the wagon planner then finds no physical wagon, the booking stays linked and
|
||||
* staff assign wagons manually. Consolidated bookings are exempt from the
|
||||
* rescue: the shared wagon is both-or-neither, and settleReserved owns that
|
||||
* pair decision.
|
||||
* staff assign wagons manually. EXCEPTION — a consolidated booking whose
|
||||
* partner lapsed unpaid is NOT rescued: its odd 20ft cannot board without the
|
||||
* partner, so the paid side is cancelled with the cancellation fee (the
|
||||
* partnerLapsed listener in BookingWagonCancellationService).
|
||||
*/
|
||||
private async expire(
|
||||
booking: Booking,
|
||||
@@ -4001,10 +4002,11 @@ export class BookingBatchService implements OnModuleInit {
|
||||
): Promise<void> {
|
||||
// Consolidated pair: break the link FIRST, then settle each side singly.
|
||||
// - neither paid → both expire, no fee.
|
||||
// - one side paid → the paid half keeps the whole wagon (rescued by the
|
||||
// paid guard below at no extra cost); the lapsed half expires and owes
|
||||
// the cancellation fee (the 'partnerLapsed' event opens the fee invoice
|
||||
// in BookingWagonCancellationService).
|
||||
// - one side paid → BOTH die: the unpaid half expires fee-free (fees only
|
||||
// apply to paid bookings); the paid half cannot board alone, so the
|
||||
// 'partnerLapsed' event cancels it with the cancellation fee on ceil of
|
||||
// its wagons (BookingWagonCancellationService) — paid freight kept as
|
||||
// rebooking credit for GL staff.
|
||||
// - both paid → nothing to expire; the paid guard rescues.
|
||||
if (booking.consolidationPartnerId) {
|
||||
const partnerId = booking.consolidationPartnerId;
|
||||
@@ -4029,19 +4031,22 @@ export class BookingBatchService implements OnModuleInit {
|
||||
if (partnerRow) partnerRow.consolidationPartnerId = null;
|
||||
|
||||
if (selfPaid && !partnerPaid) {
|
||||
// Wrong side called first: the lapsed partner is the one that expires
|
||||
// (with its fee); this paid booking falls through to the rescue below.
|
||||
// Wrong side called first: the unpaid partner expires fee-free; this
|
||||
// PAID booking cannot board without it, so the listener cancels it
|
||||
// with the cancellation fee — never rescued.
|
||||
if (partnerRow && !["EXPIRED", "CANCELLED"].includes(partnerRow.status)) {
|
||||
this.eventEmitter?.emit("booking.consolidation.partnerLapsed", {
|
||||
expiredBookingId: partnerRow.id,
|
||||
});
|
||||
await this.expire(partnerRow, reason);
|
||||
}
|
||||
} else if (!selfPaid && partnerPaid) {
|
||||
this.eventEmitter?.emit("booking.consolidation.partnerLapsed", {
|
||||
expiredBookingId: booking.id,
|
||||
paidBookingId: booking.id,
|
||||
});
|
||||
return;
|
||||
} else if (!selfPaid && partnerPaid) {
|
||||
// This unpaid side expires below, fee-free; the PAID partner cannot
|
||||
// board alone, so the listener cancels it with the cancellation fee.
|
||||
this.eventEmitter?.emit("booking.consolidation.partnerLapsed", {
|
||||
paidBookingId: partnerId,
|
||||
});
|
||||
// fall through: this side expires below; the paid partner is untouched.
|
||||
} else if (!selfPaid && !partnerPaid) {
|
||||
if (partnerRow && !["EXPIRED", "CANCELLED"].includes(partnerRow.status)) {
|
||||
await this.expire(partnerRow, reason);
|
||||
@@ -4335,8 +4340,8 @@ export class BookingBatchService implements OnModuleInit {
|
||||
swept.add(booking.id);
|
||||
// Consolidated pair: the partner may sit outside this route-day's result
|
||||
// set (different yards/day/status), so cascade explicitly — an unpaid
|
||||
// partner expires with this booking; a PAID partner keeps the whole
|
||||
// wagon and this booking owes the cancellation fee (partnerLapsed).
|
||||
// partner expires with this booking, fee-free; a PAID partner cannot
|
||||
// board alone, so partnerLapsed cancels it with the cancellation fee.
|
||||
if (booking.consolidationPartnerId) {
|
||||
const partner = await this.dataSource.getRepository(Booking).findOne({
|
||||
where: { id: booking.consolidationPartnerId },
|
||||
@@ -4352,7 +4357,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
partner.paymentStatus === "PAID" || partner.status === "PAID";
|
||||
if (partnerPaid) {
|
||||
this.eventEmitter?.emit("booking.consolidation.partnerLapsed", {
|
||||
expiredBookingId: booking.id,
|
||||
paidBookingId: partner.id,
|
||||
});
|
||||
} else if (!["EXPIRED", "CANCELLED"].includes(partner.status)) {
|
||||
swept.add(partner.id);
|
||||
|
||||
Reference in New Issue
Block a user