mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
feat(billing): enhance invoice payment processing and revert handling
feat(bookings): add event handlers for booking invoice payment processing fix(bookings): include PAYMENT_VERIFICATION_IN_PROGRESS status in queries fix(train-scheduling): update status checks to include PAYMENT_VERIFICATION_IN_PROGRESS feat(notifier): notify customers when a train is cancelled
This commit is contained in:
@@ -422,7 +422,9 @@ export class BookingBatchService implements OnModuleInit {
|
||||
.getRepository(Booking)
|
||||
.createQueryBuilder("b")
|
||||
.select("DISTINCT b.train_schedule_id", "scheduleId")
|
||||
.where(`b.status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT')`)
|
||||
.where(
|
||||
`b.status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT', 'PAYMENT_VERIFICATION_IN_PROGRESS')`,
|
||||
)
|
||||
.andWhere("b.train_schedule_id IS NOT NULL")
|
||||
.getRawMany<{ scheduleId: string }>();
|
||||
for (const { scheduleId } of reserved) this.armSettle(scheduleId);
|
||||
@@ -2118,7 +2120,9 @@ export class BookingBatchService implements OnModuleInit {
|
||||
if (linked) return "ALLOCATED";
|
||||
if (
|
||||
booking.status === "SELECTED_FOR_BATCH" ||
|
||||
booking.status === "AWAITING_PAYMENT"
|
||||
booking.status === "AWAITING_PAYMENT" ||
|
||||
// Redirect-acked, webhook pending — still a reserved (unpaid) hold.
|
||||
booking.status === "PAYMENT_VERIFICATION_IN_PROGRESS"
|
||||
) {
|
||||
return "SELECTED_FOR_BATCH";
|
||||
}
|
||||
|
||||
@@ -300,6 +300,19 @@ export class BookingNotifierService {
|
||||
this.inApp(b, 'Removed from train', msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* The train carrying this booking was cancelled. The booking is detached and
|
||||
* returns to the eligible pool — the customer must rebook or pick a new schedule.
|
||||
*/
|
||||
scheduleCancelled(b: Booking): void {
|
||||
const msg =
|
||||
`The train for booking ${b.reference ?? b.id} has been cancelled. ` +
|
||||
`Your booking is not lost — please rebook or select a new schedule from the portal.`;
|
||||
void this.notifyContact(b, msg, 'TRAIN CANCELLED');
|
||||
// HIGH: a cancelled train invalidates the customer's plans — must reach SMS/email.
|
||||
this.inApp(b, 'Train cancelled', msg, { priority: NotificationPriority.HIGH });
|
||||
}
|
||||
|
||||
/**
|
||||
* The train carrying this booking was moved for maintenance to a new departure
|
||||
* date. The booking stays on the train — only the date moved.
|
||||
|
||||
@@ -610,7 +610,9 @@ export class BookingWindowService implements OnModuleInit {
|
||||
.getRepository(Booking)
|
||||
.createQueryBuilder('b')
|
||||
.select('DISTINCT b.train_schedule_id', 'scheduleId')
|
||||
.where(`b.status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT')`)
|
||||
.where(
|
||||
`b.status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT', 'PAYMENT_VERIFICATION_IN_PROGRESS')`,
|
||||
)
|
||||
// Deadline is the line — expire() itself reconciles against the gateway
|
||||
// before actually expiring, so a late in-window payment is still caught.
|
||||
.andWhere('b.payment_deadline <= now()')
|
||||
|
||||
@@ -2653,7 +2653,9 @@ export class TrainSchedulingService {
|
||||
paymentDeadline: null,
|
||||
})
|
||||
.where('train_schedule_id = :scheduleId', { scheduleId })
|
||||
.andWhere(`status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT')`)
|
||||
.andWhere(
|
||||
`status IN ('SELECTED_FOR_BATCH', 'AWAITING_PAYMENT', 'PAYMENT_VERIFICATION_IN_PROGRESS')`,
|
||||
)
|
||||
.execute();
|
||||
});
|
||||
|
||||
@@ -4247,6 +4249,15 @@ export class TrainSchedulingService {
|
||||
}
|
||||
});
|
||||
|
||||
// Best-effort customer notice (SMS + email + in-app) — the cancel itself has
|
||||
// already committed, so a notification failure must never fail the cancel.
|
||||
for (const sb of schedule.scheduleBookings ?? []) {
|
||||
const booking = await this.bookingsRepository
|
||||
.findByIdWithFiles(sb.bookingId)
|
||||
.catch(() => null);
|
||||
if (booking) this.bookingNotifier.scheduleCancelled(booking);
|
||||
}
|
||||
|
||||
// Window retired (DONE) — remove the card from portal/GL lists right away.
|
||||
void this.emitWindowState(id);
|
||||
return this.getTrainScheduleById(id);
|
||||
|
||||
Reference in New Issue
Block a user