mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 10:40:58 +00:00
train
This commit is contained in:
@@ -208,6 +208,8 @@ export interface BatchBoardScheduleDetail {
|
||||
docReviewEndsAt: string | null;
|
||||
paymentPhaseEndsAt: string | null;
|
||||
bookingCycleNo: number;
|
||||
/** Built train (Train Builder) behind this departure, when scheduled by train. */
|
||||
train: BatchBoardSchedule["train"];
|
||||
locomotive: BatchBoardSchedule["locomotive"];
|
||||
capacity: BatchBoardSchedule["capacity"];
|
||||
counts: BatchBoardSchedule["counts"];
|
||||
@@ -235,6 +237,12 @@ export interface BatchBoardSchedule {
|
||||
docReviewEndsAt: string | null;
|
||||
paymentPhaseEndsAt: string | null;
|
||||
bookingCycleNo: number;
|
||||
/** Built train (Train Builder) behind this departure, when scheduled by train. */
|
||||
train: {
|
||||
id: string;
|
||||
code: string;
|
||||
trainName: string | null;
|
||||
} | null;
|
||||
locomotive: {
|
||||
code: string;
|
||||
name: string | null;
|
||||
@@ -877,7 +885,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
const [schedules, total] = await this.trainSchedulesRepository.findAndCount({
|
||||
where,
|
||||
relations: {
|
||||
trainSet: { locomotive: true },
|
||||
trainSet: { locomotive: true, train: true },
|
||||
originStation: true,
|
||||
destinationStation: true,
|
||||
// Yards supply the route's display name for `routeName` below;
|
||||
@@ -1128,6 +1136,13 @@ export class BookingBatchService implements OnModuleInit {
|
||||
? s.paymentPhaseEndsAt.toISOString()
|
||||
: null,
|
||||
bookingCycleNo: s.bookingCycleNo ?? 0,
|
||||
train: s.trainSet?.train
|
||||
? {
|
||||
id: s.trainSet.train.id,
|
||||
code: s.trainSet.train.code,
|
||||
trainName: s.trainSet.train.trainName ?? null,
|
||||
}
|
||||
: null,
|
||||
locomotive: loco
|
||||
? {
|
||||
code: loco.code,
|
||||
@@ -1247,6 +1262,13 @@ export class BookingBatchService implements OnModuleInit {
|
||||
? s.paymentPhaseEndsAt.toISOString()
|
||||
: null,
|
||||
bookingCycleNo: s.bookingCycleNo ?? 0,
|
||||
train: s.trainSet?.train
|
||||
? {
|
||||
id: s.trainSet.train.id,
|
||||
code: s.trainSet.train.code,
|
||||
trainName: s.trainSet.train.trainName ?? null,
|
||||
}
|
||||
: null,
|
||||
locomotive: loco
|
||||
? {
|
||||
code: loco.code,
|
||||
|
||||
@@ -282,6 +282,8 @@ interface BookingWindowRow {
|
||||
origin_code: string | null;
|
||||
destination_label: string | null;
|
||||
destination_code: string | null;
|
||||
/** Full ordered corridor (origin → milestones → destination) from the schedule's route. */
|
||||
route_stations: string[] | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -1329,9 +1331,15 @@ export class TrainSchedulingService {
|
||||
limitLoco.maxPullWeightTons + (Number(limitLoco.overageToleranceTons) || 0);
|
||||
const lengthCapWithOverage =
|
||||
limitLoco.maxTrainLengthMeters + (Number(limitLoco.overageToleranceMeters) || 0);
|
||||
if (!dto.forceAssign && weightCapWithOverage < totalWeightTons) {
|
||||
// The locomotives pull GROSS weight: the customers' cargo plus the empty
|
||||
// weight of every planned wagon — cargo-only comparison understates the load.
|
||||
const planTareTons = roundTons(
|
||||
wagonPlan.reduce((sum, slot) => sum + Number(slot.tareWeightTons ?? 0), 0),
|
||||
);
|
||||
const grossWeightTons = roundTons(totalWeightTons + planTareTons);
|
||||
if (!dto.forceAssign && weightCapWithOverage < grossWeightTons) {
|
||||
throw new BadRequestException(
|
||||
`Train set locomotives cannot pull ${totalWeightTons}T`,
|
||||
`Train set locomotives cannot pull ${grossWeightTons}T gross (${totalWeightTons}T cargo + ${planTareTons}T wagon tare)`,
|
||||
);
|
||||
}
|
||||
if (!dto.forceAssign && lengthCapWithOverage < totalLengthMeters) {
|
||||
@@ -4605,14 +4613,8 @@ export class TrainSchedulingService {
|
||||
name: link.locomotive!.name ?? null,
|
||||
})),
|
||||
wagonCount: wagons.length,
|
||||
maxGrossTons: roundTons(
|
||||
wagons.reduce(
|
||||
(sum, w) =>
|
||||
sum +
|
||||
(Number(w.wagonType?.tareWeightTons) || 0) +
|
||||
(Number(w.wagonType?.capacityTons) || 0),
|
||||
0,
|
||||
),
|
||||
totalTareTons: roundTons(
|
||||
wagons.reduce((sum, w) => sum + (Number(w.wagonType?.tareWeightTons) || 0), 0),
|
||||
),
|
||||
totalLengthMeters: roundTons(
|
||||
wagons.reduce((sum, w) => sum + (Number(w.wagonType?.lengthMeters) || 0), 0),
|
||||
@@ -4725,7 +4727,11 @@ export class TrainSchedulingService {
|
||||
ts.booking_cycle_no,
|
||||
ts.scheduled_departure_date,
|
||||
oy.label AS origin_label, oy.code AS origin_code,
|
||||
dy.label AS destination_label, dy.code AS destination_code
|
||||
dy.label AS destination_label, dy.code AS destination_code,
|
||||
(SELECT array_agg(COALESCE(rmy.label, rmy.code) ORDER BY rm.sequence_no)
|
||||
FROM freight.route_milestones rm
|
||||
JOIN freight.yards rmy ON rmy.id = rm.yard_id
|
||||
WHERE rm.route_id = ts.route_id) AS route_stations
|
||||
FROM freight.train_schedules ts
|
||||
LEFT JOIN freight.contract_routes cr
|
||||
ON cr.deleted_at IS NULL
|
||||
@@ -4777,7 +4783,11 @@ export class TrainSchedulingService {
|
||||
ts.booking_cycle_no,
|
||||
ts.scheduled_departure_date,
|
||||
oy.label AS origin_label, oy.code AS origin_code,
|
||||
dy.label AS destination_label, dy.code AS destination_code
|
||||
dy.label AS destination_label, dy.code AS destination_code,
|
||||
(SELECT array_agg(COALESCE(rmy.label, rmy.code) ORDER BY rm.sequence_no)
|
||||
FROM freight.route_milestones rm
|
||||
JOIN freight.yards rmy ON rmy.id = rm.yard_id
|
||||
WHERE rm.route_id = ts.route_id) AS route_stations
|
||||
FROM freight.train_schedules ts
|
||||
JOIN freight.contract_routes cr
|
||||
ON cr.contract_id = $1
|
||||
@@ -4823,7 +4833,11 @@ export class TrainSchedulingService {
|
||||
ts.booking_cycle_no,
|
||||
ts.scheduled_departure_date,
|
||||
oy.label AS origin_label, oy.code AS origin_code,
|
||||
dy.label AS destination_label, dy.code AS destination_code
|
||||
dy.label AS destination_label, dy.code AS destination_code,
|
||||
(SELECT array_agg(COALESCE(rmy.label, rmy.code) ORDER BY rm.sequence_no)
|
||||
FROM freight.route_milestones rm
|
||||
JOIN freight.yards rmy ON rmy.id = rm.yard_id
|
||||
WHERE rm.route_id = ts.route_id) AS route_stations
|
||||
FROM freight.train_schedules ts
|
||||
LEFT JOIN freight.yards oy ON oy.id = ts.origin_station_id
|
||||
LEFT JOIN freight.yards dy ON dy.id = ts.destination_station_id
|
||||
@@ -4845,6 +4859,17 @@ export class TrainSchedulingService {
|
||||
}
|
||||
|
||||
private mapBookingWindowRow(r: BookingWindowRow) {
|
||||
const origin = r.origin_label ?? r.origin_code ?? null;
|
||||
const destination = r.destination_label ?? r.destination_code ?? null;
|
||||
// Full corridor from the route's milestones (origin → stops → destination).
|
||||
// Falls back to the schedule's origin/destination when no milestones exist.
|
||||
const milestoneStops = (r.route_stations ?? []).filter(
|
||||
(s): s is string => Boolean(s),
|
||||
);
|
||||
const routeStations =
|
||||
milestoneStops.length >= 2
|
||||
? milestoneStops
|
||||
: [origin, destination].filter((s): s is string => Boolean(s));
|
||||
return {
|
||||
scheduleId: r.schedule_id,
|
||||
reference: r.reference ?? null,
|
||||
@@ -4860,8 +4885,9 @@ export class TrainSchedulingService {
|
||||
bookingWindowStatus: r.booking_window_status,
|
||||
bookingCycleNo: r.booking_cycle_no,
|
||||
departureDate: r.scheduled_departure_date,
|
||||
origin: r.origin_label ?? r.origin_code ?? null,
|
||||
destination: r.destination_label ?? r.destination_code ?? null,
|
||||
origin,
|
||||
destination,
|
||||
routeStations,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user