Enhance wagon capacity handling and validation in booking service

- Added default capacities for container and bulk wagons.
- Updated wagonsFor method to consider weight and length for wagon calculations.
- Improved handling of overweight bookings with appropriate warnings.
- Adjusted tests to reflect changes in wagon capacity and validation logic.
This commit is contained in:
Marshal
2026-07-09 13:10:32 +00:00
parent db35b90b45
commit eb819d66a1
6 changed files with 155 additions and 26 deletions

View File

@@ -731,3 +731,76 @@ describe('BookingBatchService — PAID reconcile', () => {
});
});
});
describe('BookingBatchService — wagonsFor', () => {
// wagonsFor is pure arithmetic over its two arguments and touches no injected
// dependency, so the service can be built with none.
const service = new BookingBatchService(
null as never,
null as never,
null as never,
null as never,
null as never,
null as never,
null as never,
null as never,
null as never,
) as unknown as {
wagonsFor(booking: unknown, dims: unknown): number;
};
// PW2 box wagon: 70T rated payload, 25.2T tare, 17.066m.
const dims = {
container: { lengthMeters: 13.966, tareWeightTons: 22.4, capacityTons: 70 },
bulk: { lengthMeters: 17.066, tareWeightTons: 25.2, capacityTons: 70 },
};
const bulk = (cargoTons: number, over: Record<string, unknown> = {}) => ({
freightType: 'BULK',
cargoTotalWeightVgm: cargoTons,
bookingContainers: [],
...over,
});
it('sizes a bulk booking by cargo ÷ rated payload, not a flat 1 wagon', () => {
// 37 × 1400 fertilizer packages × 50kg = 2590T of cargo.
expect(service.wagonsFor(bulk(2590), dims)).toBe(37);
});
it('rounds a partial wagon up', () => {
expect(service.wagonsFor(bulk(70.1), dims)).toBe(2);
expect(service.wagonsFor(bulk(70), dims)).toBe(1);
});
it('still floors at one wagon when a bulk booking has no recorded cargo', () => {
expect(service.wagonsFor(bulk(0), dims)).toBe(1);
});
it('honours an explicit wagonsRequired override', () => {
expect(service.wagonsFor(bulk(2590, { wagonsRequired: 40 }), dims)).toBe(40);
});
it('takes the binding axis for containers: weight can exceed TEU geometry', () => {
// Two 40ft units => 2 wagons by TEU geometry, but 210T needs 3 at 70T each.
const booking = {
freightType: 'CONTAINER',
cargoTotalWeightVgm: 210,
bookingContainers: [
{ quantity: 2, wagonsRequired: 2, containerType: { wagonsPerUnit: 1, sizeFt: 40 } },
],
};
expect(service.wagonsFor(booking, dims)).toBe(3);
});
it('keeps TEU geometry when it binds before weight', () => {
// Four 20ft units => 2 wagons by geometry; 40T of cargo needs only 1 by weight.
const booking = {
freightType: 'CONTAINER',
cargoTotalWeightVgm: 40,
bookingContainers: [
{ quantity: 4, wagonsRequired: 2, containerType: { wagonsPerUnit: 0.5, sizeFt: 20 } },
],
};
expect(service.wagonsFor(booking, dims)).toBe(2);
});
});