mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 07:38:10 +00:00
fix train build issue
This commit is contained in:
@@ -19,6 +19,7 @@ import { combinedLocomotiveLimits } from '../train-scheduling/train-capacity.uti
|
||||
import { ScheduleWagonAdjustmentLog } from '../train-schedules/entities/schedule-wagon-adjustment-log.entity';
|
||||
import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity';
|
||||
import { TrainSet } from '../train-sets/entities/train-set.entity';
|
||||
import { TrainSetLocomotive } from '../train-sets/entities/train-set-locomotive.entity';
|
||||
import { TrainSetWagon } from '../train-sets/entities/train-set-wagon.entity';
|
||||
import { WagonType } from '../wagon-types/entities/wagon-type.entity';
|
||||
import { WagonMovement } from '../wagons/entities/wagon-movement.entity';
|
||||
@@ -504,7 +505,7 @@ export class TrainBuilderService {
|
||||
if (locomotiveIds.length < 1) {
|
||||
throw new BadRequestException('A train must be pulled by at least one locomotive');
|
||||
}
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
const pending = await this.dataSource.transaction(async (manager) => {
|
||||
const train = await this.getEditableTrain(manager, id);
|
||||
const yard = await manager
|
||||
.getRepository(Yard)
|
||||
@@ -523,10 +524,76 @@ export class TrainBuilderService {
|
||||
await manager
|
||||
.getRepository(Train)
|
||||
.update(train.id, { capacityTons: round(limits?.maxPullWeightTons ?? 0) });
|
||||
// Capacity math reads the SET's locomotives, not the train's — push the
|
||||
// new pull weight onto the live runs too, or they keep the old ceiling.
|
||||
return this.syncLiveSchedulesAfterLocomotiveChange(manager, train.id, locomotiveIds);
|
||||
});
|
||||
// Re-derive FULL/reopen once committed — a bigger pull weight can free room
|
||||
// on a schedule that had closed as FULL.
|
||||
await this.reconcileWindowsAfterConsistChange(pending);
|
||||
return this.getComposition(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror a built train's locomotive change onto every LIVE (DRAFT/SCHEDULED)
|
||||
* schedule formed from it. The three capacity axes are derived from
|
||||
* `train_set_locomotives` (see trainSetLocomotiveLimits), which is snapshotted
|
||||
* when the set is built and never re-synced — so adding a second locomotive
|
||||
* raised `trains.capacity_tons` but left every existing schedule pulling on
|
||||
* the old single-loco ceiling, still refusing bookings for want of weight.
|
||||
*
|
||||
* Only DRAFT/SCHEDULED runs follow the live train; DISPATCHED/ARRIVED render
|
||||
* from their frozen snapshot and must not be disturbed (same rule as
|
||||
* syncLiveScheduleAfterConsistChange).
|
||||
*/
|
||||
private async syncLiveSchedulesAfterLocomotiveChange(
|
||||
manager: EntityManager,
|
||||
trainId: string,
|
||||
locomotiveIds: string[],
|
||||
): Promise<PendingWindowCheck[]> {
|
||||
const trainSets = await manager.getRepository(TrainSet).find({ where: { trainId } });
|
||||
if (!trainSets.length) return [];
|
||||
|
||||
const schedules = await manager.getRepository(TrainSchedule).find({
|
||||
where: {
|
||||
trainSetId: In(trainSets.map((s) => s.id)),
|
||||
status: In(['DRAFT', 'SCHEDULED']),
|
||||
},
|
||||
});
|
||||
if (!schedules.length) return [];
|
||||
|
||||
// Only the sets still backing a live run — a set behind an ARRIVED schedule
|
||||
// keeps the locomotives it actually ran with.
|
||||
const liveSetIds = [...new Set(schedules.map((s) => s.trainSetId))];
|
||||
const [primaryId] = locomotiveIds;
|
||||
for (const trainSetId of liveSetIds) {
|
||||
await manager.getRepository(TrainSetLocomotive).delete({ trainSetId });
|
||||
await manager.getRepository(TrainSetLocomotive).save(
|
||||
locomotiveIds.map((locomotiveId, index) =>
|
||||
manager
|
||||
.getRepository(TrainSetLocomotive)
|
||||
.create({ trainSetId, locomotiveId, sequenceNo: index }),
|
||||
),
|
||||
);
|
||||
// `locomotiveId` is the primary-locomotive fallback for single-loco reads.
|
||||
await manager.getRepository(TrainSet).update(trainSetId, { locomotiveId: primaryId });
|
||||
}
|
||||
|
||||
return schedules.map((s) => ({
|
||||
scheduleId: s.id,
|
||||
wasFull: s.bookingWindowStatus === 'FULL',
|
||||
}));
|
||||
}
|
||||
|
||||
/** {@link reconcileWindowAfterConsistChange} over several schedules. */
|
||||
private async reconcileWindowsAfterConsistChange(
|
||||
pending: PendingWindowCheck[],
|
||||
): Promise<void> {
|
||||
for (const check of pending) {
|
||||
await this.reconcileWindowAfterConsistChange(check);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a built train's display identity: name and fixed import/export run
|
||||
* numbers. Mirrors the build-time number rules — the pair may not collide
|
||||
|
||||
Reference in New Issue
Block a user