mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 16:00:56 +00:00
Coaches, seats, schedules, and pricing related updates
This commit is contained in:
@@ -87,7 +87,7 @@ async function holdSeatsTransaction(scheduleId: string, seatIds: string[], passe
|
||||
|
||||
for (const seat of seats) {
|
||||
if (seat.status !== 'AVAILABLE') {
|
||||
throw new Error(`Seat ${seat.label} is not available (status: ${seat.status})`);
|
||||
throw new Error(`Seat ${seat.seatNumber} is not available (status: ${seat.status})`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,12 @@ export class EnhancedSeatsService {
|
||||
for (const seatId of request.seatIds) {
|
||||
const seat = await tx.seat.findUnique({ where: { id: seatId } });
|
||||
if (!seat) throw new BadRequestException(`Seat ${seatId} not found`);
|
||||
// Only BLOCKED seats are hard-rejected — BOOKED/HELD are fine if the
|
||||
// segment does not overlap (another passenger may occupy a different leg)
|
||||
if (seat.status === 'BLOCKED') throw new BadRequestException(`Seat ${seat.label} is blocked`);
|
||||
if (seat.status === 'BLOCKED') throw new BadRequestException(`Seat ${seat.seatNumber} is blocked`);
|
||||
|
||||
const free = await this.segmentsService.isSeatFreeForLeg(
|
||||
request.scheduleId, seatId, reqFrom, reqTo,
|
||||
);
|
||||
if (!free) throw new ConflictException(`Seat ${seat.label} is not available for the requested leg`);
|
||||
if (!free) throw new ConflictException(`Seat ${seat.seatNumber} is not available for the requested leg`);
|
||||
}
|
||||
|
||||
const expiresAt = new Date(Date.now() + 10 * 60 * 1000);
|
||||
@@ -51,7 +49,6 @@ export class EnhancedSeatsService {
|
||||
scheduleId: request.scheduleId,
|
||||
seatIds: request.seatIds,
|
||||
passengerId: request.passengerId,
|
||||
// Store leg in createdBy JSON — no fareQuoteId needed
|
||||
createdBy: JSON.stringify({
|
||||
originStationId: request.originStationId,
|
||||
destinationStationId: request.destinationStationId,
|
||||
@@ -60,7 +57,6 @@ export class EnhancedSeatsService {
|
||||
},
|
||||
});
|
||||
|
||||
// Do NOT set seat.status = HELD globally — status is segment-scoped
|
||||
this.eventEmitter.emit('seats.held', { holdId: seatHold.id, scheduleId: request.scheduleId, seatIds: request.seatIds, segments });
|
||||
return { holdId: seatHold.id, expiresAt, segments, seats: request.seatIds };
|
||||
});
|
||||
@@ -81,7 +77,6 @@ export class EnhancedSeatsService {
|
||||
});
|
||||
if (!schedule) throw new BadRequestException('Schedule not found');
|
||||
|
||||
// Resolve the passenger's leg from createdBy JSON
|
||||
let originStationId: string | undefined;
|
||||
let destinationStationId: string | undefined;
|
||||
try {
|
||||
@@ -92,15 +87,15 @@ export class EnhancedSeatsService {
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
const originStop = originStationId ? schedule.stopTimes.find(s => s.stationId === originStationId) : undefined;
|
||||
const destStop = destinationStationId ? schedule.stopTimes.find(s => s.stationId === destinationStationId) : undefined;
|
||||
const originStop = originStationId ? schedule.stopTimes.find((s: any) => s.stationId === originStationId) : undefined;
|
||||
const destStop = destinationStationId ? schedule.stopTimes.find((s: any) => s.stationId === destinationStationId) : undefined;
|
||||
const fromSeq = originStop?.sequence ?? schedule.stopTimes[0].sequence;
|
||||
const toSeq = destStop?.sequence ?? schedule.stopTimes[schedule.stopTimes.length - 1].sequence;
|
||||
|
||||
const segments: Segment[] = [];
|
||||
for (let i = fromSeq; i < toSeq; i++) {
|
||||
const fromStop = schedule.stopTimes.find(s => s.sequence === i);
|
||||
const toStop = schedule.stopTimes.find(s => s.sequence === i + 1);
|
||||
const fromStop = schedule.stopTimes.find((s: any) => s.sequence === i);
|
||||
const toStop = schedule.stopTimes.find((s: any) => s.sequence === i + 1);
|
||||
if (fromStop && toStop) {
|
||||
segments.push({
|
||||
fromStationId: fromStop.stationId,
|
||||
@@ -132,7 +127,6 @@ export class EnhancedSeatsService {
|
||||
}
|
||||
}
|
||||
|
||||
// Do NOT set seat.status = BOOKED globally — availability is segment-scoped
|
||||
await tx.seatHold.delete({ where: { id: request.holdId } });
|
||||
|
||||
this.eventEmitter.emit('booking.confirmed', { bookingId: request.bookingId, scheduleId: hold.scheduleId, seatIds: hold.seatIds, segments });
|
||||
@@ -185,22 +179,20 @@ export class EnhancedSeatsService {
|
||||
|
||||
const schedule = await this.prisma.trainSchedule.findUnique({
|
||||
where: { id: scheduleId },
|
||||
include: { coachAssignments: { include: { coach: { include: { seats: true, seatClass: true } } } } },
|
||||
include: { coachAssignments: { include: { coach: { include: { seats: true } } } } },
|
||||
});
|
||||
if (!schedule) throw new BadRequestException('Schedule not found');
|
||||
|
||||
const availableSeats = [];
|
||||
for (const assignment of schedule.coachAssignments) {
|
||||
for (const seat of assignment.coach.seats) {
|
||||
// Hard-blocked seats are never available
|
||||
if (seat.status === 'BLOCKED') continue;
|
||||
// Availability is determined purely by segment overlap — not global seat.status
|
||||
const free = await this.segmentsService.isSeatFreeForLeg(scheduleId, seat.id, reqFrom, reqTo);
|
||||
if (free) {
|
||||
availableSeats.push({
|
||||
id: seat.id, label: seat.label,
|
||||
coach: assignment.coach.label,
|
||||
seatClass: assignment.coach.seatClass.name,
|
||||
id: seat.id, label: seat.seatNumber,
|
||||
coach: assignment.coach.number,
|
||||
seatClass: 'Standard',
|
||||
row: seat.row, col: seat.col,
|
||||
kind: seat.kind,
|
||||
isWindow: seat.isWindow,
|
||||
|
||||
Reference in New Issue
Block a user