Fix blocked seat count on booking result

This commit is contained in:
Roba Boru
2026-07-19 18:50:37 +03:00
parent 1e08f98705
commit f57f8d4550
2 changed files with 35 additions and 6 deletions

View File

@@ -390,8 +390,8 @@ export class SearchService {
// Exclude schedules with no seats at all
if (allValidSeatIds.length === 0) return null;
// Run availability batch and fare calculation in parallel
const [freeSeats, faresByClass] = await Promise.all([
// Run availability batch, fare calculation, and schedule-scoped seat blocks in parallel
const [freeSeatsRaw, faresByClass, scheduleBlocks] = await Promise.all([
this.segmentsService.getFreeSeatIds(
schedule.id,
allValidSeatIds,
@@ -400,7 +400,17 @@ export class SearchService {
destStop.sequence,
),
this.calculateFaresForSegment(schedule, originStationId, destinationStationId, nationality),
this.prisma.seatBlock.findMany({
where: {
scheduleId: schedule.id,
seatId: { in: allValidSeatIds },
},
select: { seatId: true },
}),
]);
const scheduleBlockedIds = new Set(scheduleBlocks.map((b: any) => b.seatId));
// Seats that are free from holds/bookings AND not schedule-blocked
const freeSeats = new Set([...freeSeatsRaw].filter(id => !scheduleBlockedIds.has(id)));
// Compute per-class availability using the pre-computed free seat set. A coach type
// has separate seat classes per nationality tier (e.g. "VIP Bed Upper (Local)" AND

View File

@@ -325,6 +325,18 @@ export class SeatsService {
throw new ConflictException(`Seat(s) ${blocked.map(s => s.seatNumber).join(', ')} are already taken`);
const seatLabelById = Object.fromEntries(seats.map(s => [s.id, s.seatNumber]));
// Schedule-scoped blocks prevent holding a seat on this specific schedule
// even if its global Seat.status is AVAILABLE.
const scheduleBlockRecords = await tx.seatBlock.findMany({
where: { scheduleId: dto.scheduleId, seatId: { in: seatIds } },
select: { seatId: true },
});
if (scheduleBlockRecords.length > 0) {
const blockedNums = scheduleBlockRecords.map(b => seatLabelById[b.seatId]).join(', ');
throw new ConflictException(`Seat(s) ${blockedNums} are blocked for this schedule`);
}
const stopTimes = await tx.tripStopTime.findMany({
where: { scheduleId: dto.scheduleId },
select: { stationId: true, sequence: true },
@@ -711,11 +723,18 @@ export class SeatsService {
const reqFrom = seqOf(schedule.originStationId) ?? 0;
const reqTo = seqOf(schedule.destinationStationId) ?? stopTimes.length;
const unavailable = await this.segmentsService.getSeatAvailabilityMap(
scheduleId, allSeatIds, stopTimes, reqFrom, reqTo,
);
const [unavailable, scheduleBlocks] = await Promise.all([
this.segmentsService.getSeatAvailabilityMap(
scheduleId, allSeatIds, stopTimes, reqFrom, reqTo,
),
this.prisma.seatBlock.findMany({
where: { scheduleId, seatId: { in: allSeatIds } },
select: { seatId: true },
}),
]);
const scheduleBlockedIds = new Set(scheduleBlocks.map(b => b.seatId));
const availableSeats = seats.filter(s => !unavailable.has(s.id));
const availableSeats = seats.filter(s => !unavailable.has(s.id) && !scheduleBlockedIds.has(s.id));
if (availableSeats.length < count) {
throw new ConflictException(`Only ${availableSeats.length} seats available, requested ${count}`);