mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 04:20:55 +00:00
remove reopen delay minutes from global rules and update related types
- Removed the field from and related components. - Updated to reflect the removal of the reopen delay input field. - Modified to include new train number fields: and . - Added interface to manage active schedules with trade direction. - Introduced interface to track wagon shortages in bookings. - Updated logic to ensure consistent UI state representation. - Created migrations to drop the column and add and columns to the table. - Added tests for the new booking window display logic and wagon planning functionality.
This commit is contained in:
@@ -223,6 +223,49 @@ export function nextCycleOpensAt(
|
||||
return opensAt.getTime() < departure.getTime() ? opensAt : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The desk-close instant of the office window containing `opensAt`; null for a
|
||||
* round-the-clock desk. Same-day desk (open < close): closeHour on `opensAt`'s
|
||||
* EAT day. Overnight desk (open > close): closeHour on the NEXT EAT day when
|
||||
* `opensAt` sits in the evening half, closeHour the same day when it sits in the
|
||||
* after-midnight half.
|
||||
*/
|
||||
export function officeCloseAfter(opensAt: Date, hours: OfficeHours): Date | null {
|
||||
if (isRoundTheClock(hours)) return null;
|
||||
const { hour, minute } = eatParts(opensAt);
|
||||
const openMinutes = hour * 60 + minute;
|
||||
if (
|
||||
hours.windowOpenHour > hours.windowCloseHour &&
|
||||
openMinutes >= hours.windowOpenHour * 60
|
||||
) {
|
||||
return eatDayToUtc(shiftEatDay(eatDay(opensAt), 1), hours.windowCloseHour);
|
||||
}
|
||||
return eatDayToUtc(eatDay(opensAt), hours.windowCloseHour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cap a window close at the desk-close hour that follows its open: the office
|
||||
* hours end a running window early rather than letting the duration outlive the
|
||||
* desk (open 16:00, 3h duration, desk 8–17 → closes 17:00, not 19:00). A
|
||||
* round-the-clock desk never caps; a desk-close at/before the open (degenerate
|
||||
* config) is ignored so the window is never clamped to zero length here.
|
||||
*/
|
||||
export function clampCloseToOfficeHours(
|
||||
opensAt: Date,
|
||||
closesAt: Date,
|
||||
hours: OfficeHours,
|
||||
): Date {
|
||||
const deskClose = officeCloseAfter(opensAt, hours);
|
||||
if (
|
||||
deskClose != null &&
|
||||
deskClose.getTime() > opensAt.getTime() &&
|
||||
closesAt.getTime() > deskClose.getTime()
|
||||
) {
|
||||
return deskClose;
|
||||
}
|
||||
return closesAt;
|
||||
}
|
||||
|
||||
export interface InitialWindowTimes {
|
||||
windowOpensAt: Date;
|
||||
windowClosesAt: Date;
|
||||
@@ -243,7 +286,8 @@ export interface InitialWindowTimes {
|
||||
* • `now` before openHour that EAT day → opens at openHour that morning
|
||||
* • `now` at/after closeHour → desk shut; opens openHour next morning
|
||||
*
|
||||
* `windowDurationHours` extends from that open, capped at departure.
|
||||
* `windowDurationHours` extends from that open, capped at the desk close hour
|
||||
* and at departure.
|
||||
*/
|
||||
export function computeImportWindowTimes(
|
||||
departure: Date,
|
||||
@@ -276,6 +320,10 @@ export function computeImportWindowTimes(
|
||||
}
|
||||
|
||||
let closesAt = new Date(opensAt.getTime() + cfg.windowDurationHours * 3_600_000);
|
||||
closesAt = clampCloseToOfficeHours(opensAt, closesAt, {
|
||||
windowOpenHour: cfg.windowOpenHour,
|
||||
windowCloseHour: cfg.windowCloseHour,
|
||||
});
|
||||
if (closesAt.getTime() > departure.getTime()) {
|
||||
closesAt = departure;
|
||||
}
|
||||
@@ -381,10 +429,11 @@ export function listBatchWindowsForBookings(
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Board-display windows: the REAL booking-window cycles derived from the
|
||||
// train_scheduling_global_rules config (window open hour, lead days, duration,
|
||||
// reopen delay) — NOT a fixed clock grid. Import shows each booking-window cycle
|
||||
// (opens at windowOpenHour EAT, lasts windowDurationHours, reopens after
|
||||
// reopenDelayMinutes until departure). Export shows the single FCFS lead window.
|
||||
// schedule's frozen window rule (open/close hour, lead days, duration, reopen
|
||||
// gap = doc review + payment) — NOT a fixed clock grid. Import shows each
|
||||
// booking-window cycle (opens at windowOpenHour EAT, lasts windowDurationHours
|
||||
// capped at the desk close, reopens after the gap until departure). Export shows
|
||||
// the single FCFS lead window.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** A board window carries an EAT calendar date in addition to the slot times. */
|
||||
@@ -402,8 +451,11 @@ export interface BoardWindowConfig {
|
||||
/** EAT hour the daily booking desk shuts; equals windowOpenHour for a 24h desk. */
|
||||
windowCloseHour: number;
|
||||
windowDurationHours: number;
|
||||
/** Gap between a cycle's close and its reopen (doc review + payment minutes). */
|
||||
reopenDelayMinutes: number;
|
||||
/**
|
||||
* Gap between a cycle's close and its reopen — always doc review + payment
|
||||
* minutes (the schedule's frozen snapshot, or the live sum for legacy rows).
|
||||
*/
|
||||
reopenGapMinutes: number;
|
||||
exportBookingLeadHours: number;
|
||||
}
|
||||
|
||||
@@ -435,10 +487,11 @@ function boardWindowFromInterval(start: Date, end: Date): BoardWindow {
|
||||
* The real booking-window cycles for a schedule, straight from config.
|
||||
*
|
||||
* IMPORT: first window opens at `windowOpenHour` EAT on `departure − importWindowLeadDays`
|
||||
* for `windowDurationHours`; if the train isn't full it reopens `reopenDelayMinutes`
|
||||
* after each close, on the same booking day, until departure. This mirrors
|
||||
* `computeImportWindowTimes` + `concludeCycle`'s reopen math so the board shows the
|
||||
* exact windows the engine runs.
|
||||
* for `windowDurationHours` (cut short by the desk close hour); if the train isn't
|
||||
* full it reopens `reopenGapMinutes` (doc review + payment) after each close,
|
||||
* honouring office hours, until departure. This mirrors `computeImportWindowTimes`
|
||||
* + `concludeCycle`'s reopen math so the board shows the exact windows the engine
|
||||
* runs.
|
||||
* EXPORT: a single FCFS window from `departure − exportBookingLeadHours` to departure,
|
||||
* with the open shifted to the next desk opening when it lands outside office hours
|
||||
* (same math as `computeExportWindowTimes`).
|
||||
@@ -464,7 +517,7 @@ export function listConfigBookingWindows(
|
||||
const durationMs = cfg.windowDurationHours * 3_600_000;
|
||||
// Post-close gap before the next cycle opens (doc review + payment), subject
|
||||
// to office hours below.
|
||||
const reopenMs = cfg.reopenDelayMinutes * 60_000;
|
||||
const reopenMs = cfg.reopenGapMinutes * 60_000;
|
||||
const officeHours: OfficeHours = {
|
||||
windowOpenHour: cfg.windowOpenHour,
|
||||
windowCloseHour: cfg.windowCloseHour,
|
||||
@@ -484,6 +537,7 @@ export function listConfigBookingWindows(
|
||||
for (let cycle = 0; cycle < maxCycles; cycle += 1) {
|
||||
if (opensAt.getTime() >= departure.getTime()) break;
|
||||
let closesAt = new Date(opensAt.getTime() + durationMs);
|
||||
closesAt = clampCloseToOfficeHours(opensAt, closesAt, officeHours);
|
||||
if (closesAt.getTime() > departure.getTime()) closesAt = departure;
|
||||
windows.push(boardWindowFromInterval(opensAt, closesAt));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user