mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 04:50:54 +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:
@@ -2,9 +2,14 @@ import { AllocationLoadType } from '@edr/types';
|
||||
|
||||
import { Booking } from '../bookings/entities/booking.entity';
|
||||
import { WagonType } from '../wagon-types/entities/wagon-type.entity';
|
||||
import { sortBookingsForScheduling, type DeferredBookingRow } from './fleet-plan.util';
|
||||
import {
|
||||
sortBookingsForScheduling,
|
||||
type BookingWagonShortage,
|
||||
type DeferredBookingRow,
|
||||
} from './fleet-plan.util';
|
||||
import {
|
||||
MAX_TEU_SLOTS_PER_WAGON,
|
||||
containerWagonsForLines,
|
||||
expandBookingContainerUnits,
|
||||
roundTons,
|
||||
tareTonsOf,
|
||||
@@ -55,7 +60,12 @@ type OpenSlot = {
|
||||
freeCapacityTons: number;
|
||||
};
|
||||
|
||||
type PlacementProblem = { kind: 'config' | 'stock'; message: string };
|
||||
type PlacementProblem = {
|
||||
kind: 'config' | 'stock';
|
||||
message: string;
|
||||
/** Wagon types the failing placement could have used (stock problems only). */
|
||||
candidates?: WagonType[];
|
||||
};
|
||||
|
||||
const slotFromWagonType = (wagonType: WagonType, kind: SlotLoadType): WagonPlanSlot => ({
|
||||
sequenceNo: 0, // stamped at the end
|
||||
@@ -69,6 +79,38 @@ const slotFromWagonType = (wagonType: WagonType, kind: SlotLoadType): WagonPlanS
|
||||
slotLoadType: kind,
|
||||
});
|
||||
|
||||
/**
|
||||
* Booking-level shortage against the wagon types the failing placement could
|
||||
* use: wagons the whole booking needs vs stock left for those types. Container
|
||||
* counts are TEU-packed per booking; bulk divides by the largest candidate.
|
||||
*/
|
||||
const shortageFor = (
|
||||
booking: Booking,
|
||||
candidates: WagonType[],
|
||||
remaining: Map<string, number>,
|
||||
): BookingWagonShortage => {
|
||||
const wagonsNeeded =
|
||||
booking.freightType === 'BULK'
|
||||
? Math.max(
|
||||
1,
|
||||
Math.ceil(
|
||||
Number(booking.cargoTotalWeightVgm ?? 0) /
|
||||
Math.max(1, ...candidates.map((wt) => Number(wt.capacityTons))),
|
||||
),
|
||||
)
|
||||
: Math.max(1, containerWagonsForLines(booking.bookingContainers ?? []));
|
||||
const wagonsAvailable = candidates.reduce(
|
||||
(sum, wt) => sum + (remaining.get(wt.id) ?? 0),
|
||||
0,
|
||||
);
|
||||
return {
|
||||
wagonTypeCodes: [...new Set(candidates.map((wt) => wt.code))].join('/'),
|
||||
wagonsNeeded,
|
||||
wagonsAvailable,
|
||||
wagonsShort: Math.max(1, wagonsNeeded - wagonsAvailable),
|
||||
};
|
||||
};
|
||||
|
||||
const addAllocation = (
|
||||
slot: WagonPlanSlot,
|
||||
bookingId: string,
|
||||
@@ -120,7 +162,9 @@ export function planWagonsWithStock(params: {
|
||||
cargoTypeId: string | null,
|
||||
): OpenSlot | PlacementProblem => {
|
||||
const inStock = candidates.filter((wt) => (remaining.get(wt.id) ?? 0) > 0);
|
||||
if (!inStock.length) return { kind: 'stock', message: noStockMessage(candidates) };
|
||||
if (!inStock.length) {
|
||||
return { kind: 'stock', message: noStockMessage(candidates), candidates };
|
||||
}
|
||||
// Bulk favors the largest wagon (fewest wagons for the tonnage); containers
|
||||
// favor the deepest stock so the consist drains evenly. Ties keep config order.
|
||||
const chosen = [...inStock].sort((a, b) =>
|
||||
@@ -271,7 +315,21 @@ export function planWagonsWithStock(params: {
|
||||
});
|
||||
|
||||
if (problem.kind === 'config') configIssues.add(problem.message);
|
||||
deferred.push({ id: booking.id, reference: booking.reference, reason: problem.message });
|
||||
// remaining is rolled back here, so the shortage counts the stock this
|
||||
// booking actually saw — not what its own partial placement consumed.
|
||||
const shortage =
|
||||
problem.kind === 'stock' && problem.candidates?.length
|
||||
? shortageFor(booking, problem.candidates, remaining)
|
||||
: null;
|
||||
deferred.push({
|
||||
id: booking.id,
|
||||
reference: booking.reference,
|
||||
reason: shortage
|
||||
? `${problem.message} — needs ${shortage.wagonsNeeded} × ${shortage.wagonTypeCodes}, ` +
|
||||
`${shortage.wagonsAvailable} available (short ${shortage.wagonsShort})`
|
||||
: problem.message,
|
||||
shortage,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user