changes export flow

This commit is contained in:
Marshal
2026-07-30 11:30:34 +00:00
parent b671f07ce6
commit 2ca04b8f98
47 changed files with 1195 additions and 74 deletions

View File

@@ -1,6 +1,8 @@
import {
bookingCargoTons,
bookingGrossWeightTons,
bookingTrainLengthMeters,
bulkItemWagonsRequired,
consistUsage,
consistViolations,
deriveTrainCapacityFromLocomotive,
@@ -30,6 +32,66 @@ describe('train-capacity.util', () => {
cargoTons,
}));
describe('bulkItemWagonsRequired (break-bulk PER_ITEM)', () => {
// cargoTotalWeightVgm carries the ITEM COUNT for PER_ITEM cargo; the real
// tonnage rides in bulkTotalWeightTons.
const breakBulk = (quantity: number, weightTons: number) => ({
freightType: 'BULK',
cargoTotalWeightVgm: quantity,
bulkTotalWeightTons: weightTons,
});
it('floors items per wagon, then ceils wagons: 400 items / 800T on 69T wagons → 12', () => {
// 800/400 = 2T per item; floor(69/2) = 34 per wagon; ceil(400/34) = 12.
expect(bulkItemWagonsRequired(breakBulk(400, 800), 69)).toBe(12);
});
it('needs more wagons than raw tonnage suggests when the floor loses capacity', () => {
// 3 items × 40T on 69T wagons: by weight ceil(120/69) = 2, but only ONE
// whole 40T item fits a wagon → 3 wagons.
expect(bulkItemWagonsRequired(breakBulk(3, 120), 69)).toBe(3);
});
it('charges one wagon per item when a single item outweighs a wagon', () => {
expect(bulkItemWagonsRequired(breakBulk(2, 200), 69)).toBe(2);
});
it('returns 0 for PER_TON bulk (no stored weight) and container bookings', () => {
expect(
bulkItemWagonsRequired(
{ freightType: 'BULK', cargoTotalWeightVgm: 500, bulkTotalWeightTons: null },
69,
),
).toBe(0);
expect(
bulkItemWagonsRequired(
{ freightType: 'CONTAINER', cargoTotalWeightVgm: 100, bulkTotalWeightTons: 100 },
69,
),
).toBe(0);
});
it('returns 0 on zero/invalid capacity or amounts', () => {
expect(bulkItemWagonsRequired(breakBulk(400, 800), 0)).toBe(0);
expect(bulkItemWagonsRequired(breakBulk(0, 800), 69)).toBe(0);
expect(bulkItemWagonsRequired(breakBulk(400, 0), 69)).toBe(0);
});
});
describe('bookingCargoTons (break-bulk weight preference)', () => {
it('prefers bulkTotalWeightTons over the item-count VGM column', () => {
expect(
bookingCargoTons({ cargoTotalWeightVgm: 400, bulkTotalWeightTons: 800 }),
).toBe(800);
});
it('falls back to cargoTotalWeightVgm when no break-bulk weight is stored', () => {
expect(
bookingCargoTons({ cargoTotalWeightVgm: 500, bulkTotalWeightTons: null }),
).toBe(500);
});
});
describe('deriveTrainCapacityFromLocomotive', () => {
it('derives wagon slots from train length, not a fixed 53', () => {
const shortLoco = deriveTrainCapacityFromLocomotive(