Added stops departure and arrival datetime

This commit is contained in:
Roba Boru
2026-07-18 21:51:54 +03:00
parent d7a66cfac5
commit c1858e76d4
16 changed files with 1026 additions and 94 deletions

View File

@@ -146,6 +146,7 @@ export class TasksService {
await Promise.all([
this.sendPaymentReminders(now),
this.cancelExpiredPendingBookings(now),
this.cancelExpiredPendingPackageBookings(now),
]);
}
@@ -333,6 +334,78 @@ export class TasksService {
}
}
// ── Cancel PackageBookings whose payment deadline has passed ──────────────
private async cancelExpiredPendingPackageBookings(now: Date) {
const twoHoursAgo = new Date(now.getTime() - MAX_PAYMENT_HOURS * 60 * 60 * 1000);
const departureCutoff = new Date(now.getTime() + CUTOFF_MINUTES * 60 * 1000);
const expiredBookings = await this.prisma.packageBooking.findMany({
where: {
status: 'PENDING_PAYMENT',
OR: [
{ createdAt: { lte: twoHoursAgo } },
{ package: { outboundSchedule: { departureAt: { lte: departureCutoff } } } },
],
},
include: {
package: {
include: {
outboundSchedule: {
include: { route: { select: { checkinMinutesBefore: true } } },
},
},
},
},
});
let cancelledCount = 0;
for (const booking of expiredBookings) {
try {
const createdAt = booking.createdAt as Date;
const dep = (booking.package as any).outboundSchedule.departureAt as Date;
const checkinMinutes = (booking.package as any).outboundSchedule.route?.checkinMinutesBefore ?? CUTOFF_MINUTES;
const paymentDeadline = computePaymentDeadline(createdAt, dep, checkinMinutes);
if (now < paymentDeadline) continue;
// Revert the tier's seat counters that were incremented when the booking was created.
const seatsReserved = booking.adultCount + Math.max(0, booking.childCount - booking.adultCount);
await this.prisma.packagePriceTier.update({
where: { id: booking.priceTierId },
data: {
bookedSeats: { decrement: seatsReserved },
availableSeats: { increment: seatsReserved },
},
});
await this.prisma.packageBooking.update({
where: { id: booking.id },
data: { status: 'CANCELLED' },
});
const message =
`EDR: Your package booking ${booking.bookingRef} ` +
`(departs ${fmtTime(dep)}) has been cancelled ` +
`because payment was not completed by ${fmtTime(paymentDeadline)}.`;
if (booking.contactPhone) {
await this.sms.sendSms({ to: booking.contactPhone, message }).catch(() => null);
}
this.logger.log(`Auto-cancelled package booking: ${booking.bookingRef} (deadline was ${fmtTime(paymentDeadline)})`);
cancelledCount++;
} catch (err) {
this.logger.error(
`Auto-cancel failed for package booking ${(booking as any).bookingRef}: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
if (cancelledCount > 0) {
this.logger.log(`Auto-cancelled ${cancelledCount} expired pending package booking(s)`);
}
}
// ─────────────────────────────────────────────────────────────────────────
// Daily at 02:00 EAT: purge expired/stale records to enforce data retention.
// ─────────────────────────────────────────────────────────────────────────