mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Adding train delay minutes and also disabling no schedule days in the booking selection
This commit is contained in:
@@ -2,10 +2,11 @@ import { Injectable, Logger, NotFoundException, BadRequestException } from '@nes
|
||||
import { PrismaService } from '../../common/prisma.service';
|
||||
import { RoutesService } from './routes.service';
|
||||
import { FareEngineService } from '../fare-engine/fare-engine.service';
|
||||
import { CreateScheduleDto, UpdateScheduleDto, CreateFareRuleDto, UpdateScheduleStatusDto, UpdateStopTimeDto, ListSchedulesDto, BulkCreateSchedulesDto } from './schedules.dto';
|
||||
import { CreateScheduleDto, UpdateScheduleDto, CreateFareRuleDto, UpdateScheduleStatusDto, UpdateStopTimeDto, ListSchedulesDto, BulkCreateSchedulesDto, ApplyDelayDto } from './schedules.dto';
|
||||
import { DeleteOperationException } from '../../common/exceptions/delete-operation.exception';
|
||||
import { parseEthiopianTime, startOfDayEAT, startOfNextDayEAT } from '../../common/utils/timezone.utils';
|
||||
import { AuditService } from '../../common/audit.service';
|
||||
import { LiveService } from '../live/live.service';
|
||||
|
||||
@Injectable()
|
||||
export class SchedulesService {
|
||||
@@ -16,6 +17,7 @@ export class SchedulesService {
|
||||
private routesService: RoutesService,
|
||||
private fareEngine: FareEngineService,
|
||||
private auditService: AuditService,
|
||||
private liveService: LiveService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
@@ -126,6 +128,7 @@ export class SchedulesService {
|
||||
include: { coach: true },
|
||||
orderBy: { positionNumber: 'asc' },
|
||||
},
|
||||
liveStatus: { select: { delayMinutes: true } },
|
||||
_count: { select: { coachAssignments: true, bookings: true } },
|
||||
},
|
||||
orderBy: { departureAt: 'asc' },
|
||||
@@ -246,6 +249,7 @@ export class SchedulesService {
|
||||
orderBy: { positionNumber: 'asc' },
|
||||
},
|
||||
stopTimes: { include: { station: true }, orderBy: { sequence: 'asc' } },
|
||||
liveStatus: { select: { delayMinutes: true } },
|
||||
},
|
||||
});
|
||||
if (!schedule) throw new NotFoundException('Schedule not found');
|
||||
@@ -462,6 +466,70 @@ export class SchedulesService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts stored planned times additively rather than reusing updateSchedulePartial's
|
||||
* recompute-from-route-interpolation path — that path also guards `departureAt must be in the
|
||||
* future`, which a delay report for an already-departed/EN_ROUTE train would legitimately
|
||||
* fail. Check-in cutoffs (resolveCheckinCutoff, SeatsService.holdSeats) are both derived
|
||||
* directly from TripStopTime.plannedArrivalAt/plannedDepartureAt at read time, so shifting the
|
||||
* stored values here is the entire fix — neither of those needs to change.
|
||||
*/
|
||||
async applyDelay(scheduleId: string, dto: ApplyDelayDto) {
|
||||
const schedule = await this.prisma.trainSchedule.findUnique({ where: { id: scheduleId } });
|
||||
if (!schedule) throw new NotFoundException('Schedule not found');
|
||||
|
||||
const stopWhere: any = { scheduleId };
|
||||
if (dto.fromSequence != null) {
|
||||
stopWhere.sequence = { gte: dto.fromSequence };
|
||||
} else {
|
||||
// Default: only stops the train hasn't reached yet — a delay report must not retroactively
|
||||
// move a stop that's already BOARDED/COMPLETED.
|
||||
stopWhere.status = { notIn: ['BOARDED', 'COMPLETED'] };
|
||||
}
|
||||
|
||||
const stopsToShift = await this.prisma.tripStopTime.findMany({ where: stopWhere });
|
||||
const shiftMs = dto.delayMinutes * 60_000;
|
||||
const includesOrigin = stopsToShift.some((s) => s.sequence === 1);
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
for (const stop of stopsToShift) {
|
||||
await tx.tripStopTime.update({
|
||||
where: { id: stop.id },
|
||||
data: {
|
||||
plannedArrivalAt: stop.plannedArrivalAt ? new Date(stop.plannedArrivalAt.getTime() + shiftMs) : undefined,
|
||||
plannedDepartureAt: stop.plannedDepartureAt ? new Date(stop.plannedDepartureAt.getTime() + shiftMs) : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Origin stop shifted → the schedule's own departureAt/arrivalAt drive search's day-window
|
||||
// queries and the displayed departure time, so they must move too (both together, so
|
||||
// durationMinutes stays correct).
|
||||
if (includesOrigin) {
|
||||
await tx.trainSchedule.update({
|
||||
where: { id: scheduleId },
|
||||
data: {
|
||||
departureAt: new Date(schedule.departureAt.getTime() + shiftMs),
|
||||
arrivalAt: new Date(schedule.arrivalAt.getTime() + shiftMs),
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const currentLive = await this.prisma.tripLiveStatus.findUnique({ where: { scheduleId } });
|
||||
const accumulatedDelayMinutes = Math.max(0, (currentLive?.delayMinutes ?? 0) + dto.delayMinutes);
|
||||
await this.liveService.updateLiveStatus(scheduleId, { delayMinutes: accumulatedDelayMinutes });
|
||||
|
||||
await this.auditService.log({
|
||||
action: 'UPDATE',
|
||||
entityType: 'Schedule',
|
||||
entityId: scheduleId,
|
||||
newData: { delayMinutes: dto.delayMinutes, fromSequence: dto.fromSequence, accumulatedDelayMinutes },
|
||||
});
|
||||
|
||||
return this.getSchedule(scheduleId);
|
||||
}
|
||||
|
||||
async upsertScheduleFare(
|
||||
scheduleId: string,
|
||||
seatClassId: string,
|
||||
|
||||
Reference in New Issue
Block a user