mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 05:18:11 +00:00
issue fix
This commit is contained in:
@@ -577,11 +577,7 @@ export class TrainBuilderService {
|
|||||||
if (!wagon || wagon.trainId !== train.id) {
|
if (!wagon || wagon.trainId !== train.id) {
|
||||||
throw new NotFoundException(`Wagon ${wagonId} is not part of this train`);
|
throw new NotFoundException(`Wagon ${wagonId} is not part of this train`);
|
||||||
}
|
}
|
||||||
if (await this.isWagonPinnedToLiveSchedule(manager, wagon.id)) {
|
await this.assertDetachableAndReleaseStaleSlots(manager, wagon);
|
||||||
throw new ConflictException(
|
|
||||||
`Wagon ${wagon.wagonNumber} is pinned to an active schedule and cannot be removed`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await manager.getRepository(Wagon).update(wagon.id, {
|
await manager.getRepository(Wagon).update(wagon.id, {
|
||||||
trainId: null,
|
trainId: null,
|
||||||
sequenceNumber: null,
|
sequenceNumber: null,
|
||||||
@@ -618,11 +614,7 @@ export class TrainBuilderService {
|
|||||||
if (!wagon || wagon.trainId !== train.id) {
|
if (!wagon || wagon.trainId !== train.id) {
|
||||||
throw new NotFoundException(`Wagon ${wagonId} is not part of this train`);
|
throw new NotFoundException(`Wagon ${wagonId} is not part of this train`);
|
||||||
}
|
}
|
||||||
if (await this.isWagonPinnedToLiveSchedule(manager, wagon.id)) {
|
await this.assertDetachableAndReleaseStaleSlots(manager, wagon);
|
||||||
throw new ConflictException(
|
|
||||||
`Wagon ${wagon.wagonNumber} is pinned to an active schedule and cannot be removed`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const previousStatus = wagon.status;
|
const previousStatus = wagon.status;
|
||||||
const notes = buildMaintenanceNotes(formatTrainRunLabel(train), note);
|
const notes = buildMaintenanceNotes(formatTrainRunLabel(train), note);
|
||||||
await manager.getRepository(Wagon).update(wagon.id, {
|
await manager.getRepository(Wagon).update(wagon.id, {
|
||||||
@@ -705,6 +697,57 @@ export class TrainBuilderService {
|
|||||||
return rows.length > 0;
|
return rows.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detach guard for removeWagon / sendWagonToMaintenance. A wagon is truly
|
||||||
|
* pinned only while a live schedule still NEEDS it: a slot carrying booking
|
||||||
|
* allocations, or any slot on a DISPATCHED run. An empty (allocation-free)
|
||||||
|
* slot on a DRAFT/SCHEDULED schedule is a stale reservation — its load was
|
||||||
|
* moved to another wagon (moveWagonLoad keeps the emptied slot) or its
|
||||||
|
* booking left through a path that didn't clean up — and used to pin the
|
||||||
|
* wagon forever. Release those slots here instead of blocking, with the
|
||||||
|
* same recount removeTrainSetWagonSlot does (wagonCount / totalLengthMeters
|
||||||
|
* feed the schedule capacity math).
|
||||||
|
*/
|
||||||
|
private async assertDetachableAndReleaseStaleSlots(
|
||||||
|
manager: EntityManager,
|
||||||
|
wagon: Wagon,
|
||||||
|
): Promise<void> {
|
||||||
|
const rows: { id: string; train_set_id: string; status: string; allocs: string }[] =
|
||||||
|
await manager.query(
|
||||||
|
`SELECT tsw.id, tsw.train_set_id, ts.status,
|
||||||
|
(SELECT count(*)
|
||||||
|
FROM freight.wagon_booking_allocations a
|
||||||
|
WHERE a.train_set_wagon_id = tsw.id
|
||||||
|
AND a.deleted_at IS NULL) AS allocs
|
||||||
|
FROM freight.train_set_wagons tsw
|
||||||
|
JOIN freight.train_schedules ts ON ts.train_set_id = tsw.train_set_id
|
||||||
|
WHERE tsw.physical_wagon_id = $1
|
||||||
|
AND ts.status IN ('DRAFT', 'SCHEDULED', 'DISPATCHED')
|
||||||
|
AND ts.deleted_at IS NULL
|
||||||
|
AND tsw.deleted_at IS NULL`,
|
||||||
|
[wagon.id],
|
||||||
|
);
|
||||||
|
if (!rows.length) return;
|
||||||
|
if (rows.some((r) => Number(r.allocs) > 0 || r.status === 'DISPATCHED')) {
|
||||||
|
throw new ConflictException(
|
||||||
|
`Wagon ${wagon.wagonNumber} is pinned to an active schedule and cannot be removed`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await manager.getRepository(TrainSetWagon).delete(rows.map((r) => r.id));
|
||||||
|
for (const trainSetId of [...new Set(rows.map((r) => r.train_set_id))]) {
|
||||||
|
const remaining = await manager.getRepository(TrainSetWagon).find({
|
||||||
|
where: { trainSetId },
|
||||||
|
select: { id: true, lengthMeters: true },
|
||||||
|
});
|
||||||
|
await manager.getRepository(TrainSet).update(trainSetId, {
|
||||||
|
wagonCount: remaining.length,
|
||||||
|
totalLengthMeters: round(
|
||||||
|
remaining.reduce((sum, w) => sum + (Number(w.lengthMeters) || 0), 0),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Persist a drag-reorder: `wagonIds` is the full consist in its new order. */
|
/** Persist a drag-reorder: `wagonIds` is the full consist in its new order. */
|
||||||
async reorderWagons(id: string, dto: ReorderTrainWagonsDto) {
|
async reorderWagons(id: string, dto: ReorderTrainWagonsDto) {
|
||||||
await this.dataSource.transaction(async (manager) => {
|
await this.dataSource.transaction(async (manager) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user