automate the schedule

This commit is contained in:
Marshal
2026-06-11 19:42:23 +00:00
parent f85c13ce8c
commit cde3e462ba
28 changed files with 1197 additions and 12 deletions

View File

@@ -588,11 +588,34 @@ export class TrainSchedulingService {
manager,
);
}
// Close the booking window; any still-pending (unallocated) reservations don't ride this train.
await manager
.getRepository(TrainSchedule)
.update(scheduleId, { bookingWindowStatus: 'CLOSED' });
await manager
.getRepository(Booking)
.createQueryBuilder()
.update()
.set({
status: 'EXPIRED',
schedulingStatus: SchedulingStatus.Eligible,
paymentDeadline: null,
})
.where('train_schedule_id = :scheduleId', { scheduleId })
.andWhere(`status = 'AWAITING_PAYMENT'`)
.execute();
});
return this.getTrainScheduleById(scheduleId);
}
/** Open or close a schedule's booking window (staff override). */
async setBookingWindow(scheduleId: string, status: 'OPEN' | 'CLOSED'): Promise<void> {
await this.dataSource
.getRepository(TrainSchedule)
.update(scheduleId, { bookingWindowStatus: status });
}
/** Build the ordered station list for a schedule's corridor (origin → milestones → destination). */
private async buildScheduleStations(schedule: TrainSchedule) {
type Station = { sequenceNo: number; yardId: string; label: string; code: string };
@@ -937,7 +960,8 @@ export class TrainSchedulingService {
}
const invalidStatus = bookings.filter(
(b) => !SCHEDULABLE_BOOKING_STATUSES.includes(b.status as 'PAID'),
(b) =>
!SCHEDULABLE_BOOKING_STATUSES.includes(b.status as 'PAID') && !b.isGovernment,
);
if (invalidStatus.length) {
const statuses = [...new Set(invalidStatus.map((b) => b.status))];
@@ -1591,9 +1615,37 @@ export class TrainSchedulingService {
bookingsCount: schedule.scheduleBookings?.length ?? 0,
freightType: this.resolveScheduleFreightType(schedule),
status: schedule.status,
bookingWindowStatus: schedule.bookingWindowStatus ?? 'OPEN',
maxWagons: schedule.maxWagons ?? 0,
remainingWagons: Math.max(
0,
(schedule.maxWagons ?? 0) - (schedule.trainSet?.wagonCount ?? 0),
),
};
}
/** OPEN, same-route schedules a new booking may target (with rough remaining capacity). */
async getBookableSchedules(originYardId?: string, destinationYardId?: string) {
const schedules = await this.trainSchedulesRepository.findAll({
where: {
bookingWindowStatus: 'OPEN',
...(originYardId ? { originStationId: originYardId } : {}),
...(destinationYardId ? { destinationStationId: destinationYardId } : {}),
},
relations: {
trainSet: { locomotive: true },
route: true,
originStation: true,
destinationStation: true,
scheduleBookings: { booking: true },
},
order: { scheduledDepartureDate: 'ASC' },
});
return schedules
.filter((s) => ['DRAFT', 'SCHEDULED'].includes(s.status))
.map((s) => this.mapScheduleListItem(s));
}
private async mapScheduleDetail(
schedule: import('../train-schedules/entities/train-schedule.entity').TrainSchedule,
) {