mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge pull request #815 from Tria-plc/alpha
Fix blocked seat count on booking result
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -33,6 +33,8 @@ export default function PassengersReportPage() {
|
||||
const [scheduleId, setScheduleId] = useState('');
|
||||
const [tab, setTab] = useState<Tab>('occupancy');
|
||||
const [listSearch, setListSearch] = useState('');
|
||||
const [filterCoach, setFilterCoach] = useState('');
|
||||
const [filterOrigin, setFilterOrigin] = useState('');
|
||||
|
||||
const { data: schedulesRaw, isLoading: loadingSchedules } = useQuery<ScheduleOption[]>({
|
||||
queryKey: ['report-schedules'],
|
||||
@@ -98,7 +100,7 @@ export default function PassengersReportPage() {
|
||||
<select
|
||||
className="input"
|
||||
value={scheduleId}
|
||||
onChange={e => { setScheduleId(e.target.value); setTab('occupancy'); setListSearch(''); }}
|
||||
onChange={e => { setScheduleId(e.target.value); setTab('occupancy'); setListSearch(''); setFilterCoach(''); setFilterOrigin(''); }}
|
||||
disabled={loadingSchedules}
|
||||
>
|
||||
<option value="">{loadingSchedules ? 'Loading schedules…' : 'Select a schedule…'}</option>
|
||||
|
||||
Reference in New Issue
Block a user