mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 06:40:57 +00:00
Empties had no way onto a departure: the return record could name a train but nothing seated it on a wagon. Export schedules now expose a loading action that packs selected returns onto free wagons at one 40ft or two 20ft each, enforced both in the picker and in the API (existing empties on the schedule count against their wagon). Adds container_size, train_schedule_id and wagon_sequence_no to freight.empty_container_returns.
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import type { EmptyContainerSize } from "@/types/importOperations";
|
|
|
|
export interface EmptyLoadPick {
|
|
id: string;
|
|
containerSize: EmptyContainerSize;
|
|
}
|
|
|
|
export interface EmptyLoadAssignment extends EmptyLoadPick {
|
|
wagonSequenceNo: number;
|
|
}
|
|
|
|
/**
|
|
* Fill wagons with the picked empties: a wagon takes ONE 40ft or TWO 20ft,
|
|
* never a mix. 40ft boxes are seated first so a half-filled 20ft wagon can
|
|
* never block them, and the 20s pair up behind them.
|
|
*
|
|
* `freeWagons` is the caller's ordered list of wagon sequence numbers with no
|
|
* cargo allocation. Returns the assignments that fit plus the picks that had
|
|
* no wagon left — the caller surfaces the shortfall instead of silently
|
|
* dropping boxes.
|
|
*/
|
|
export function packEmptiesOntoWagons(
|
|
picks: EmptyLoadPick[],
|
|
freeWagons: number[],
|
|
): { assignments: EmptyLoadAssignment[]; unplaced: EmptyLoadPick[] } {
|
|
const forty = picks.filter((pick) => pick.containerSize === "40");
|
|
const twenty = picks.filter((pick) => pick.containerSize === "20");
|
|
|
|
const assignments: EmptyLoadAssignment[] = [];
|
|
const unplaced: EmptyLoadPick[] = [];
|
|
const wagons = [...freeWagons];
|
|
|
|
for (const pick of forty) {
|
|
const wagon = wagons.shift();
|
|
if (wagon == null) unplaced.push(pick);
|
|
else assignments.push({ ...pick, wagonSequenceNo: wagon });
|
|
}
|
|
|
|
for (let index = 0; index < twenty.length; index += 2) {
|
|
const pair = twenty.slice(index, index + 2);
|
|
const wagon = wagons.shift();
|
|
if (wagon == null) unplaced.push(...pair);
|
|
else assignments.push(...pair.map((pick) => ({ ...pick, wagonSequenceNo: wagon })));
|
|
}
|
|
|
|
return { assignments, unplaced };
|
|
}
|
|
|
|
/** Wagons the picks consume, whether or not enough are free. */
|
|
export function wagonsNeeded(picks: EmptyLoadPick[]): number {
|
|
const forty = picks.filter((pick) => pick.containerSize === "40").length;
|
|
const twenty = picks.length - forty;
|
|
return forty + Math.ceil(twenty / 2);
|
|
}
|