Package pricing issue resolution, passenger report, tickets filter updates

This commit is contained in:
Stephanos A
2026-07-17 23:18:52 +03:00
parent 2567e25173
commit f2c037a5b7
10 changed files with 510 additions and 38 deletions

View File

@@ -18,6 +18,12 @@ export class ReportsController {
return this.service.generateReport(dto);
}
@Get('occupancy')
@ApiOperation({ summary: 'Occupancy report for a specific schedule' })
getOccupancyReport(@Query('scheduleId') scheduleId: string) {
return this.service.getOccupancyBySchedule(scheduleId);
}
@Get(':reportId')
@ApiOperation({ summary: 'Get report by ID' })
getReport(@Param('reportId') reportId: string) {

View File

@@ -191,6 +191,122 @@ export class ReportsService {
};
}
async getOccupancyBySchedule(scheduleId: string) {
const schedule = await this.prisma.trainSchedule.findUnique({
where: { id: scheduleId },
include: {
originStation: true,
destinationStation: true,
train: true,
coachAssignments: {
include: {
coach: {
include: {
coachType: true,
seats: { select: { id: true } },
},
},
},
},
bookings: {
where: { status: { in: ['CONFIRMED', 'BOARDED'] } },
include: {
seats: {
include: {
seat: { include: { coach: { include: { coachType: true } } } },
},
},
},
},
stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } },
},
});
if (!schedule) return null;
const totalSeats = (schedule as any).coachAssignments.reduce((s: number, a: any) => s + a.coach.seats.length, 0);
const allBookingSeats = (schedule as any).bookings.flatMap((b: any) => b.seats);
const totalPassengers = allBookingSeats.length;
const occupancyRate = totalSeats > 0 ? +((totalPassengers / totalSeats) * 100).toFixed(1) : 0;
// Per-coach breakdown
const coachMap = new Map<string, { coachNumber: string; coachType: string; totalSeats: number; booked: number }>();
for (const assignment of (schedule as any).coachAssignments) {
const c = assignment.coach;
coachMap.set(c.id, {
coachNumber: c.number,
coachType: (c as any).coachType?.name ?? 'Unknown',
totalSeats: c.seats.length,
booked: 0,
});
}
for (const bs of allBookingSeats) {
const coachId = bs.seat?.coachId;
if (coachId && coachMap.has(coachId)) coachMap.get(coachId)!.booked++;
}
const byCoach = [...coachMap.values()].map(c => ({
...c,
occupancyRate: c.totalSeats > 0 ? +((c.booked / c.totalSeats) * 100).toFixed(1) : 0,
}));
// Per-origin station breakdown (using booking's originStationId)
const originMap = new Map<string, { stationName: string; passengers: number }>();
for (const booking of (schedule as any).bookings) {
const stationId = booking.originStationId ?? schedule.originStationId;
const stationName = (schedule as any).stopTimes.find((st: any) => st.stationId === stationId)?.station?.name
?? (schedule as any).originStation?.name
?? stationId;
if (!originMap.has(stationId)) originMap.set(stationId, { stationName, passengers: 0 });
originMap.get(stationId)!.passengers += booking.seats.length;
}
const byOrigin = [...originMap.values()].sort((a, b) => b.passengers - a.passengers);
// Per-destination station breakdown
const destMap = new Map<string, { stationName: string; passengers: number }>();
for (const booking of (schedule as any).bookings) {
const stationId = booking.destinationStationId ?? schedule.destinationStationId;
const stationName = (schedule as any).stopTimes.find((st: any) => st.stationId === stationId)?.station?.name
?? (schedule as any).destinationStation?.name
?? stationId;
if (!destMap.has(stationId)) destMap.set(stationId, { stationName, passengers: 0 });
destMap.get(stationId)!.passengers += booking.seats.length;
}
const byDestination = [...destMap.values()].sort((a, b) => b.passengers - a.passengers);
// Per-class breakdown
const classMap = new Map<string, { className: string; totalSeats: number; booked: number }>();
for (const assignment of (schedule as any).coachAssignments) {
const typeName = (assignment.coach as any).coachType?.name ?? 'Unknown';
if (!classMap.has(typeName)) classMap.set(typeName, { className: typeName, totalSeats: 0, booked: 0 });
classMap.get(typeName)!.totalSeats += assignment.coach.seats.length;
}
for (const bs of allBookingSeats) {
const typeName = bs.seat?.coach?.coachType?.name ?? 'Unknown';
if (!classMap.has(typeName)) classMap.set(typeName, { className: typeName, totalSeats: 0, booked: 0 });
classMap.get(typeName)!.booked++;
}
const byClass = [...classMap.values()].map(c => ({
...c,
occupancyRate: c.totalSeats > 0 ? +((c.booked / c.totalSeats) * 100).toFixed(1) : 0,
}));
return {
schedule: {
id: schedule.id,
trainName: (schedule as any).train?.name ?? (schedule as any).train?.number,
origin: (schedule as any).originStation?.name,
destination: (schedule as any).destinationStation?.name,
departureAt: schedule.departureAt,
arrivalAt: schedule.arrivalAt,
},
summary: { totalSeats, totalPassengers, occupancyRate },
byCoach,
byClass,
byOrigin,
byDestination,
};
}
async getReport(reportId: string) {
return this.prisma.operationalReport.findUnique({ where: { id: reportId } });
}