import { BaseRepository } from '@edr/api-common'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { EntityManager, Repository } from 'typeorm'; import { TrainSchedule, TrainScheduleStatus } from './entities/train-schedule.entity'; @Injectable() export class TrainSchedulesRepository extends BaseRepository { constructor( @InjectRepository(TrainSchedule) repository: Repository, ) { super(repository); } private repo(manager?: EntityManager) { return manager ? manager.getRepository(TrainSchedule) : this.repository; } findByIdWithFullGraph(id: string, manager?: EntityManager): Promise { return this.repo(manager).findOne({ where: { id }, relations: { // Yards carry the route's display name; without them formatRouteLabel // degrades to the literal "Origin → Destination". Milestones (with // their yards) give it the full corridor path. route: { originYard: true, destinationYard: true, milestones: { yard: true } }, trainSet: { locomotive: true, locomotives: { locomotive: true }, wagons: { wagonType: true, physicalWagon: true, allocations: { booking: { company: true, bookingContainers: { containerType: true } }, containerItems: true, }, }, }, originStation: true, destinationStation: true, scheduleBookings: { booking: { company: true, originYard: true, destinationYard: true, bookingContainers: { containerType: true }, cargoType: true, }, }, }, }); } /** * Light fetch for human-facing labels (notifications): reference, train * number, departure and the two station names — none of the composition * graph {@link findByIdWithFullGraph} drags in. */ findByIdWithStations(id: string): Promise { return this.repository.findOne({ where: { id }, relations: { originStation: true, destinationStation: true }, }); } async updateStatus( id: string, status: TrainScheduleStatus, extra?: Partial, manager?: EntityManager, ): Promise { await this.repo(manager).update(id, { status, ...extra } as never); } /** * Highest NNNNN sequence already issued for `S--…` references. Includes * soft-deleted rows so the next number never reuses one still occupying the * unique index (see the same pattern on BookingsRepository). */ async maxReferenceSequence(year: number): Promise { const row = await this.repository .createQueryBuilder('schedule') .withDeleted() .select( "COALESCE(MAX(CAST(SUBSTRING(schedule.reference FROM '[0-9]+$') AS int)), 0)", 'max', ) .where('schedule.reference LIKE :prefix', { prefix: `S-${year}-%` }) .getRawOne<{ max: string | number | null }>(); return Number(row?.max ?? 0); } }