diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts index 04583c777..a0b1591f3 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts @@ -435,7 +435,12 @@ export class TrainSchedulingService { .where('s.originStationId = :originStationId', { originStationId }) .andWhere('s.destinationStationId = :destinationStationId', { destinationStationId }) .andWhere('s.scheduledDepartureDate >= :dayStart', { dayStart }) - .andWhere('s.scheduledDepartureDate < :nextDayStart', { nextDayStart }); + .andWhere('s.scheduledDepartureDate < :nextDayStart', { nextDayStart }) + // A cancelled train is not a sibling: cancel retires its window as DONE, + // and a newborn anchoring to it would inherit that dead window verbatim. + .andWhere('s.status != :cancelledStatus', { + cancelledStatus: TrainScheduleStatusEnum.Cancelled, + }); if (excludeScheduleId) { qb.andWhere('s.id != :excludeScheduleId', { excludeScheduleId }); } @@ -471,7 +476,12 @@ export class TrainSchedulingService { departure, ); if (siblings.length === 0) return null; - const withWindow = siblings.filter((s) => s.windowOpensAt != null); + // A DONE window is retired (the day's last cycle already ran) — anchoring + // to it would hand the newborn a dead window no tick ever advances. With no + // live or pending sibling left, fall back to fresh times (return null). + const withWindow = siblings.filter( + (s) => s.windowOpensAt != null && s.windowPhase !== 'DONE', + ); if (withWindow.length === 0) return null; // A group whose window is live (some sibling has moved past PRE_WINDOW but is