Enhance bulk booking handling and wagon capacity calculations across services

This commit is contained in:
Marshal
2026-07-09 15:30:00 +00:00
parent 3259bd7667
commit 1690f9e498
11 changed files with 278 additions and 32 deletions

View File

@@ -256,6 +256,42 @@ export function bookingGrossWeightTons(
return round3(num(cargoTons) + wagonCount * num(tarePerWagonTons));
}
/**
* Size a partial (split-on-payment) offer against the room left on a train,
* across ALL THREE capacity axes — not just wagon slots. Each wagon adds
* `capacityTons` of payload headroom but its own tare spends the same weight
* room the cargo needs, so on a weight-limited train more wagons is not always
* more cargo. Scans wagon counts (the last wagon may run part-loaded) and
* returns the count that maximizes the cargo carried, with the cargo cap the
* caller should apply. Null when not even one part-loaded wagon fits. The
* offer is a strict subset of the booking: never all `bookingWagons`.
*/
export function sizePartialOfferWagons(
room: { wagons: number; weightTons: number; lengthMeters: number },
bookingWagons: number,
perWagon: { capacityTons: number; tareWeightTons: number; lengthMeters: number },
): { wagons: number; maxCargoTons: number } | null {
const maxByLength =
perWagon.lengthMeters > 0
? Math.floor(room.lengthMeters / perWagon.lengthMeters)
: room.wagons;
const ceiling = Math.min(room.wagons, maxByLength, bookingWagons - 1);
let wagons = 0;
let bestCargoTons = 0;
for (let w = 1; w <= ceiling; w += 1) {
const cargoAt = Math.min(
w * perWagon.capacityTons,
room.weightTons - w * perWagon.tareWeightTons,
);
if (cargoAt > bestCargoTons) {
bestCargoTons = cargoAt;
wagons = w;
}
}
if (wagons < 1) return null;
return { wagons, maxCargoTons: round3(room.weightTons - wagons * perWagon.tareWeightTons) };
}
export function wagonTypeDimensionsFromEntity(wt: {
lengthMeters?: number | string | null;
capacityTons?: number | string | null;