mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 15:18:11 +00:00
Extend seat hold time if booking created
This commit is contained in:
@@ -3,11 +3,7 @@ import { Cron } from '@nestjs/schedule';
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { SmsClientService } from '../notifications/sms-client.service';
|
||||
import { CurrencyService } from '../currency/currency.service';
|
||||
|
||||
/** Maximum time (hours) a passenger has to pay after booking. */
|
||||
const MAX_PAYMENT_HOURS = 2;
|
||||
/** Minutes before departure: cutoff for new bookings and payment deadline. */
|
||||
const CUTOFF_MINUTES = 30;
|
||||
import { MAX_PAYMENT_HOURS, CUTOFF_MINUTES, computePaymentDeadline } from '../../common/utils/payment-deadline.utils';
|
||||
|
||||
// Retention windows
|
||||
const OTP_RETENTION_HOURS = 1;
|
||||
@@ -16,15 +12,6 @@ const AUDIT_LOG_RETENTION_DAYS = 365;
|
||||
const WEBHOOK_EVENT_RETENTION_DAYS = 90;
|
||||
const GATE_LOG_RETENTION_DAYS = 180;
|
||||
|
||||
/**
|
||||
* payment_deadline = MIN(booking_time + 2h, departure_time - 30min)
|
||||
*/
|
||||
function computePaymentDeadline(createdAt: Date, departureAt: Date): Date {
|
||||
const maxDeadline = new Date(createdAt.getTime() + MAX_PAYMENT_HOURS * 60 * 60 * 1000);
|
||||
const cutoffDeadline = new Date(departureAt.getTime() - CUTOFF_MINUTES * 60 * 1000);
|
||||
return maxDeadline < cutoffDeadline ? maxDeadline : cutoffDeadline;
|
||||
}
|
||||
|
||||
function fmtTime(d: Date): string {
|
||||
return d.toLocaleTimeString('en-GB', {
|
||||
hour: '2-digit',
|
||||
@@ -192,6 +179,7 @@ export class TasksService {
|
||||
},
|
||||
},
|
||||
paymentIntent: { select: { method: true } },
|
||||
seats: { select: { seatId: true } },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -205,9 +193,21 @@ export class TasksService {
|
||||
const paymentDeadline = computePaymentDeadline(createdAt, dep);
|
||||
if (now < paymentDeadline) continue;
|
||||
|
||||
// 1. Release held seats (Journey rows are the occupancy source of truth)
|
||||
// 1a. Release held seats (Journey rows are the occupancy source of truth once paid)
|
||||
await this.prisma.journey.deleteMany({ where: { bookingId: booking.id } as any });
|
||||
|
||||
// 1b. Also release the SeatHold(s) covering this booking's seats — SeatsService
|
||||
// extends these to the payment deadline when the booking is created, so without
|
||||
// this they'd otherwise keep the seat locked for up to MAX_PAYMENT_HOURS even
|
||||
// though the booking is now cancelled. Scoped to this booking's own schedule,
|
||||
// since the same physical Seat row is reused across other recurring dates.
|
||||
const seatIds = booking.seats.map(s => s.seatId);
|
||||
if (seatIds.length > 0) {
|
||||
await this.prisma.seatHold.deleteMany({
|
||||
where: { scheduleId: booking.scheduleId, seatIds: { hasSome: seatIds } },
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Audit record (no refund — payment was never completed)
|
||||
await this.prisma.bookingCancellation.create({
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user