mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
chore: update package overrides in pnpm-workspace.yaml for dependencies
This commit is contained in:
@@ -1646,4 +1646,92 @@ describe('TrainSchedulingService', () => {
|
|||||||
).toBe(5);
|
).toBe(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('maintenanceReschedule — window reopens when it had already finished', () => {
|
||||||
|
const { TrainSchedule } = jest.requireActual(
|
||||||
|
'../../train-schedules/entities/train-schedule.entity',
|
||||||
|
);
|
||||||
|
|
||||||
|
const doneExportSchedule = (extra: Record<string, unknown> = {}) => ({
|
||||||
|
id: 'sch-done',
|
||||||
|
status: 'SCHEDULED',
|
||||||
|
direction: 'EXPORT',
|
||||||
|
windowPhase: 'DONE',
|
||||||
|
bookingWindowStatus: 'CLOSED',
|
||||||
|
scheduledDepartureDate: new Date('2027-06-20T05:00:00.000Z'),
|
||||||
|
scheduledArrivalDate: null,
|
||||||
|
originStationId: 'yard-origin',
|
||||||
|
destinationStationId: 'yard-destination',
|
||||||
|
scheduleBookings: [],
|
||||||
|
// Frozen rule snapshot: desk 8–17 EAT, 24h lead, close 120min before departure.
|
||||||
|
ruleWindowOpenHour: 8,
|
||||||
|
ruleWindowCloseHour: 17,
|
||||||
|
ruleExportBookingLeadHours: 24,
|
||||||
|
ruleExportCloseOffsetMinutes: 120,
|
||||||
|
...extra,
|
||||||
|
});
|
||||||
|
|
||||||
|
let scheduleUpdate: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduleUpdate = jest.fn().mockResolvedValue({ affected: 1 });
|
||||||
|
dataSource.getRepository.mockImplementation((entity: unknown) => {
|
||||||
|
if (entity === TrainSchedulingGlobalRules) {
|
||||||
|
return { find: jest.fn().mockResolvedValue([]) };
|
||||||
|
}
|
||||||
|
if (entity === TrainSchedule) {
|
||||||
|
return { update: scheduleUpdate, find: jest.fn().mockResolvedValue([]) };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
find: jest.fn().mockResolvedValue([]),
|
||||||
|
findOne: jest.fn().mockResolvedValue(null),
|
||||||
|
update: jest.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
trainSchedulesRepository.findById.mockResolvedValue(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reopens a DONE export window against the new departure', async () => {
|
||||||
|
trainSchedulesRepository.findByIdWithFullGraph.mockResolvedValue(
|
||||||
|
doneExportSchedule(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// New departure 12:00 EAT → window opens 24h earlier (12:00 EAT, inside
|
||||||
|
// the desk) and closes at departure − 120min = 10:00 EAT.
|
||||||
|
await service.maintenanceReschedule('sch-done', {
|
||||||
|
newDepartureDate: '2027-06-20T09:00:00.000Z',
|
||||||
|
} as never);
|
||||||
|
|
||||||
|
expect(scheduleUpdate).toHaveBeenCalledWith(
|
||||||
|
'sch-done',
|
||||||
|
expect.objectContaining({
|
||||||
|
scheduledDepartureDate: new Date('2027-06-20T09:00:00.000Z'),
|
||||||
|
windowPhase: 'PRE_WINDOW',
|
||||||
|
bookingWindowStatus: 'CLOSED',
|
||||||
|
windowOpensAt: new Date('2027-06-19T09:00:00.000Z'),
|
||||||
|
windowClosesAt: new Date('2027-06-20T07:00:00.000Z'),
|
||||||
|
docReviewCompletedAt: null,
|
||||||
|
docReviewEndsAt: null,
|
||||||
|
paymentPhaseEndsAt: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps a FULL train closed — nothing left to sell', async () => {
|
||||||
|
trainSchedulesRepository.findByIdWithFullGraph.mockResolvedValue(
|
||||||
|
doneExportSchedule({ bookingWindowStatus: 'FULL' }),
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.maintenanceReschedule('sch-done', {
|
||||||
|
newDepartureDate: '2027-06-20T09:00:00.000Z',
|
||||||
|
} as never);
|
||||||
|
|
||||||
|
const written = scheduleUpdate.mock.calls[0][1];
|
||||||
|
expect(written.scheduledDepartureDate).toEqual(
|
||||||
|
new Date('2027-06-20T09:00:00.000Z'),
|
||||||
|
);
|
||||||
|
expect(written.windowPhase).toBeUndefined();
|
||||||
|
expect(written.windowOpensAt).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1182,13 +1182,23 @@ export class TrainSchedulingService {
|
|||||||
? new Date(new Date(schedule.scheduledArrivalDate).getTime() + deltaMs)
|
? new Date(new Date(schedule.scheduledArrivalDate).getTime() + deltaMs)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// PRE_WINDOW only: the stamped open/close were derived from the old
|
// PRE_WINDOW: the stamped open/close were derived from the old departure
|
||||||
// departure and the window hasn't opened yet, so re-derive them from the
|
// and the window hasn't opened yet, so re-derive them from the schedule's
|
||||||
// schedule's own rule snapshot against the new date (joining the target
|
// own rule snapshot against the new date (joining the target day's route
|
||||||
// day's route group timeline when one exists, exactly like
|
// group timeline when one exists, exactly like updateScheduleDate).
|
||||||
// updateScheduleDate). Mid/post-window schedules keep their timeline.
|
//
|
||||||
|
// DONE: the window already finished (e.g. the close offset hit and then the
|
||||||
|
// train was moved to a later departure). The window must follow the new
|
||||||
|
// departure, so it REOPENS: re-derive open/close the same way, reset the
|
||||||
|
// phase to PRE_WINDOW and clamp a past open into the present so the tick
|
||||||
|
// opens it immediately. A FULL train stays closed — there is nothing left
|
||||||
|
// to sell — and so does one whose re-derived window would already be over.
|
||||||
|
//
|
||||||
|
// Mid-window phases (OPEN/DOC_REVIEW/PAYMENT) keep their running timeline.
|
||||||
|
const reopenFromDone =
|
||||||
|
schedule.windowPhase === 'DONE' && schedule.bookingWindowStatus !== 'FULL';
|
||||||
const windowFields =
|
const windowFields =
|
||||||
schedule.windowPhase === 'PRE_WINDOW'
|
schedule.windowPhase === 'PRE_WINDOW' || reopenFromDone
|
||||||
? await (async () => {
|
? await (async () => {
|
||||||
const merged = effectiveWindowConfig(
|
const merged = effectiveWindowConfig(
|
||||||
schedule,
|
schedule,
|
||||||
@@ -1207,12 +1217,33 @@ export class TrainSchedulingService {
|
|||||||
schedule.destinationStationId,
|
schedule.destinationStationId,
|
||||||
departure,
|
departure,
|
||||||
);
|
);
|
||||||
return anchor
|
if (anchor) {
|
||||||
? this.groupWindowFieldsFrom(anchor, departure)
|
// groupWindowFieldsFrom copies the anchor's live phase and
|
||||||
: {
|
// deadlines, so a DONE train joining a live group re-enters the
|
||||||
windowOpensAt: times.windowOpensAt,
|
// group's cycle directly — no extra reset needed.
|
||||||
windowClosesAt: times.windowClosesAt,
|
return this.groupWindowFieldsFrom(anchor, departure);
|
||||||
};
|
}
|
||||||
|
if (!reopenFromDone) {
|
||||||
|
return {
|
||||||
|
windowOpensAt: times.windowOpensAt,
|
||||||
|
windowClosesAt: times.windowClosesAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const now = new Date();
|
||||||
|
const windowOpensAt =
|
||||||
|
times.windowOpensAt < now ? now : times.windowOpensAt;
|
||||||
|
if (times.windowClosesAt.getTime() <= windowOpensAt.getTime()) {
|
||||||
|
return {}; // no window fits before the new departure — stay closed
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
windowOpensAt,
|
||||||
|
windowClosesAt: times.windowClosesAt,
|
||||||
|
windowPhase: 'PRE_WINDOW',
|
||||||
|
bookingWindowStatus: 'CLOSED',
|
||||||
|
docReviewCompletedAt: null,
|
||||||
|
docReviewEndsAt: null,
|
||||||
|
paymentPhaseEndsAt: null,
|
||||||
|
};
|
||||||
})()
|
})()
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
|||||||
2250
pnpm-lock.yaml
generated
2250
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -26,3 +26,10 @@ allowBuilds:
|
|||||||
tesseract.js: true
|
tesseract.js: true
|
||||||
uglifyjs-webpack-plugin: true
|
uglifyjs-webpack-plugin: true
|
||||||
unrs-resolver: false
|
unrs-resolver: false
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
date-fns: ^3.6.0
|
||||||
|
pdfjs-dist: ^3.11.174
|
||||||
|
react: 19.2.6
|
||||||
|
react-dom: 19.2.6
|
||||||
|
typeorm: 0.3.30
|
||||||
|
|||||||
Reference in New Issue
Block a user