Files
edr-platform/apps/edr-freight-api/src/modules/train-schedules/train-schedules.repository.ts

95 lines
3.1 KiB
TypeScript

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<TrainSchedule> {
constructor(
@InjectRepository(TrainSchedule)
repository: Repository<TrainSchedule>,
) {
super(repository);
}
private repo(manager?: EntityManager) {
return manager ? manager.getRepository(TrainSchedule) : this.repository;
}
findByIdWithFullGraph(id: string, manager?: EntityManager): Promise<TrainSchedule | null> {
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<TrainSchedule | null> {
return this.repository.findOne({
where: { id },
relations: { originStation: true, destinationStation: true },
});
}
async updateStatus(
id: string,
status: TrainScheduleStatus,
extra?: Partial<TrainSchedule>,
manager?: EntityManager,
): Promise<void> {
await this.repo(manager).update(id, { status, ...extra } as never);
}
/**
* Highest NNNNN sequence already issued for `S-<year>-…` 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<number> {
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);
}
}