Reports (overall, seats report, passenger export) updates

This commit is contained in:
Stephanos A
2026-07-20 00:37:10 +03:00
parent 57390647d8
commit c62491b236
8 changed files with 890 additions and 825 deletions

View File

@@ -30,6 +30,12 @@ export class ReportsController {
return this.service.getPassengerList(scheduleId);
}
@Get('seats')
@ApiOperation({ summary: 'Seat status report for a specific schedule' })
getSeatStatusReport(@Query('scheduleId') scheduleId: string) {
return this.service.getSeatStatusReport(scheduleId);
}
@Get('passengers')
@ApiOperation({ summary: 'Passengers report for a specific schedule' })
getOccupancyReport(@Query('scheduleId') scheduleId: string) {

View File

@@ -378,6 +378,101 @@ export class ReportsService {
}));
}
async getSeatStatusReport(scheduleId: string) {
// Booked seats — exclude dining coaches
const bookingSeats = await this.prisma.bookingSeat.findMany({
where: {
leg: 1,
booking: { scheduleId, status: { in: ['CONFIRMED', 'BOARDED', 'PENDING_PAYMENT'] } },
seat: { coach: { coachType: { type: { not: 'dining' } } } },
},
include: {
booking: {
select: {
bookingRef: true,
status: true,
totalMinor: true,
currency: true,
createdAt: true,
paymentIntent: { select: { status: true } },
},
},
seat: {
select: {
seatNumber: true,
bedPosition: true,
coach: {
select: {
number: true,
coachType: { select: { name: true, type: true, seatClasses: { select: { name: true, bedPosition: true } } } },
},
},
},
},
},
orderBy: [{ seat: { coach: { number: 'asc' } } }, { seat: { seatNumber: 'asc' } }],
});
// Manually blocked seats for this schedule — exclude MAINTENANCE entries
const blocks = await this.prisma.seatBlock.findMany({
where: {
scheduleId,
NOT: { reason: { startsWith: 'MAINTENANCE:' } },
},
include: {
seat: {
select: {
seatNumber: true,
bedPosition: true,
coach: {
select: {
number: true,
coachType: { select: { name: true, type: true, seatClasses: { select: { name: true, bedPosition: true } } } },
},
},
},
},
},
orderBy: { blockedAt: 'desc' },
});
const resolveSeatClass = (seat: any): string | null => {
const classes = seat?.coach?.coachType?.seatClasses ?? [];
const matched = seat?.bedPosition
? classes.find((sc: any) => sc.bedPosition?.toLowerCase() === seat.bedPosition.toLowerCase())
: null;
return (matched ?? classes[0])?.name ?? seat?.coach?.coachType?.name ?? null;
};
return {
bookedSeats: bookingSeats.map(bs => ({
bookingRef: bs.booking.bookingRef,
passengerName: bs.passengerName,
passengerCategory: bs.passengerCategory,
coachNumber: bs.seat?.coach?.number ?? null,
seatNumber: bs.seat?.seatNumber ?? null,
seatClassName: resolveSeatClass(bs.seat),
fareMinor: bs.fareMinor,
currency: bs.booking.currency ?? 'ETB',
bookingStatus: bs.booking.status,
paymentStatus: bs.booking.paymentIntent?.status ?? 'PENDING',
bookedAt: bs.booking.createdAt,
})),
blockedSeats: blocks
.filter(b => b.seat?.coach?.coachType?.type !== 'dining')
.map(b => ({
id: b.id,
coachNumber: b.seat?.coach?.number ?? null,
seatNumber: b.seat?.seatNumber ?? null,
seatClassName: resolveSeatClass(b.seat),
reason: b.reason,
blockedBy: b.blockedBy,
blockedAt: b.blockedAt,
unblockAt: b.unblockAt,
})),
};
}
async getReport(reportId: string) {
return this.prisma.operationalReport.findUnique({ where: { id: reportId } });
}