mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 13:10:56 +00:00
Ticket generation updates
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
Logger,
|
||||
NotFoundException,
|
||||
BadRequestException,
|
||||
ConflictException,
|
||||
} from "@nestjs/common";
|
||||
import { PrismaService } from "../../common/prisma.service";
|
||||
import { SeatsService } from "../seats/seats.service";
|
||||
@@ -897,12 +898,25 @@ export class PaymentsService {
|
||||
try {
|
||||
await this.ticketsService.generate(booking.id);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`Error generating ticket for booking ${booking.id}: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
// Re-throw so callers (e.g. force-confirm) know tickets weren't issued.
|
||||
// Webhook handlers catch this themselves and still return 200 to avoid redelivery.
|
||||
throw err;
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
// Only reassign seats when a *different* booking genuinely holds the seat
|
||||
// (ConflictException). Any other error (transient DB issue, etc.) is logged
|
||||
// and swallowed — the passenger keeps their original seat and the ticket can
|
||||
// be retried via "Generate Missing" in the backoffice.
|
||||
if (err instanceof ConflictException) {
|
||||
this.logger.warn(
|
||||
`Seat conflict for booking ${booking.id}: ${msg}. Attempting smart seat reassignment.`,
|
||||
);
|
||||
try {
|
||||
await this.ticketsService.smartAssignAndGenerate(booking.id);
|
||||
} catch (retryErr) {
|
||||
this.logger.error(
|
||||
`Smart assign also failed for booking ${booking.id}: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.logger.error(`Error generating ticket for booking ${booking.id}: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -352,8 +352,20 @@ export class TicketsService {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for seat conflicts before deleting existing tickets or issuing new ones
|
||||
await this.prisma.ticket.deleteMany({ where: { bookingId } });
|
||||
|
||||
// Remove any SeatBlock rows left over from a previous generate() run for this
|
||||
// booking — they reference the old ticket IDs which are now deleted, and would
|
||||
// otherwise cause the conflict check below to see this booking's own seats as
|
||||
// blocked by another booking.
|
||||
const seatIds = (booking as any).seats.map((bs: any) => bs.seatId);
|
||||
await this.prisma.seatBlock.deleteMany({
|
||||
where: { seatId: { in: seatIds }, blockedBy: 'SYSTEM', approvedBy: 'SYSTEM' },
|
||||
});
|
||||
|
||||
// Check for seat conflicts — only seats confirmed/boarded by a *different* booking
|
||||
// on the same schedule are a real conflict. SeatBlock rows created by a previous
|
||||
// generate() run for this booking are NOT a conflict; they are cleaned up above.
|
||||
const conflictingSeats = await this.prisma.bookingSeat.findMany({
|
||||
where: {
|
||||
seatId: { in: seatIds },
|
||||
@@ -371,8 +383,6 @@ export class TicketsService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.prisma.ticket.deleteMany({ where: { bookingId } });
|
||||
|
||||
// Generate one ticket per passenger per leg.
|
||||
// Round-trip / transit bookings have seats on multiple legs — each leg needs its own
|
||||
// ticket so the voucher can match by (passengerName, leg) and gate scanners can
|
||||
|
||||
@@ -70,7 +70,9 @@ export default function ConfirmationPage() {
|
||||
const CONFIRMATION_GRACE_PERIOD_MS = 10_000;
|
||||
const FAST_POLL_INTERVAL_MS = 2_500;
|
||||
const SLOW_POLL_INTERVAL_MS = 10_000;
|
||||
const MAX_TICKET_POLL_ATTEMPTS = 12; // 12 × 2.5s = 30s max wait for tickets
|
||||
const mountTimeRef = useRef(Date.now());
|
||||
const ticketPollAttemptsRef = useRef(0);
|
||||
const [withinGracePeriod, setWithinGracePeriod] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -122,7 +124,13 @@ export default function ConfirmationPage() {
|
||||
if (!data || data.status !== "CONFIRMED") return false;
|
||||
const adultCount = searchCriteria?.adultCount ?? passengers.filter((p) => !isChild(p)).length;
|
||||
const expectedTickets = Math.max(1, adultCount);
|
||||
return (data.tickets?.length ?? 0) >= expectedTickets ? false : FAST_POLL_INTERVAL_MS;
|
||||
if ((data.tickets?.length ?? 0) >= expectedTickets) {
|
||||
ticketPollAttemptsRef.current = 0;
|
||||
return false;
|
||||
}
|
||||
if (ticketPollAttemptsRef.current >= MAX_TICKET_POLL_ATTEMPTS) return false;
|
||||
ticketPollAttemptsRef.current += 1;
|
||||
return FAST_POLL_INTERVAL_MS;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user