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

@@ -6,6 +6,7 @@ import {
deriveTrainCapacityFromLocomotive,
grossWagonWeightTons,
minLocomotiveLimits,
sizePartialOfferWagons,
} from './train-capacity.util';
describe('train-capacity.util', () => {
@@ -185,4 +186,54 @@ describe('train-capacity.util', () => {
expect(limits?.maxPullWeightTons).toBe(3500);
expect(limits?.overageToleranceTons).toBe(20);
});
describe('sizePartialOfferWagons', () => {
it('sizes a bulk split by the WEIGHT axis when the pull limit binds, not wagon slots', () => {
// The 3500T-train scenario: two 1000T bookings boarded gross (each 15 PW2
// wagons: 1000 + 378 tare = 1378), leaving 744T of pull weight but plenty
// of slots/length. The boundary 1000T booking (15 wagons) must be offered
// the largest part 744T can carry: 8 wagons whose tare is 201.6T, hauling
// 542.4T of cargo — gross exactly 744.
const offer = sizePartialOfferWagons(
{ wagons: 40, weightTons: 744, lengthMeters: 500 },
15,
pw2,
);
expect(offer).toEqual({ wagons: 8, maxCargoTons: 542.4 });
});
it('still sizes by wagon slots when they bind first (legacy behavior)', () => {
const offer = sizePartialOfferWagons(
{ wagons: 3, weightTons: 100000, lengthMeters: 100000 },
15,
pw2,
);
expect(offer?.wagons).toBe(3);
});
it('sizes by the LENGTH axis when it binds first', () => {
// 60m of train left → 3 PW2 (17.066m) fit, the 4th does not.
const offer = sizePartialOfferWagons(
{ wagons: 40, weightTons: 100000, lengthMeters: 60 },
15,
pw2,
);
expect(offer?.wagons).toBe(3);
});
it('never offers all of the booking — a split is a strict subset', () => {
const offer = sizePartialOfferWagons(
{ wagons: 40, weightTons: 100000, lengthMeters: 100000 },
15,
pw2,
);
expect(offer?.wagons).toBe(14);
});
it('returns null when not even one part-loaded wagon fits the weight room', () => {
expect(
sizePartialOfferWagons({ wagons: 5, weightTons: 20, lengthMeters: 500 }, 15, pw2),
).toBeNull();
});
});
});