mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
leg-aware capacity and wagon sharing
This commit is contained in:
@@ -198,7 +198,9 @@ describe('planWagonsWithStock — leg-aware stock (intercity ride-along)', () =>
|
||||
new Map(entries);
|
||||
|
||||
it('lets an intercity booking ride the empty leg of a train that is full on the other leg', () => {
|
||||
// 1 wagon in stock. Export rides edge 1 only; intercity rides edge 0 only.
|
||||
// 1 wagon in stock. Export rides edge 1 only; intercity rides edge 0 only:
|
||||
// the intercity 20ft alights where the export 20ft boards, so both share
|
||||
// the single physical wagon (cross-leg TEU sharing).
|
||||
const result = planWagonsWithStock({
|
||||
bookings: [
|
||||
containerBooking('EXPORT-1', 1, 1),
|
||||
@@ -222,16 +224,19 @@ describe('planWagonsWithStock — leg-aware stock (intercity ride-along)', () =>
|
||||
'EXPORT-1',
|
||||
'INTERCITY-1',
|
||||
]);
|
||||
// Two slots planned, but both drawn from the single physical wagon.
|
||||
expect(result.plan).toHaveLength(2);
|
||||
expect(result.plan).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('still defers when the legs overlap and stock is exhausted', () => {
|
||||
it('still defers when the wagon has no per-edge TEU room and stock is exhausted', () => {
|
||||
// Export is a 40ft (2 TEU) riding the whole corridor — no edge has room
|
||||
// for the intercity 20ft, and there is no second wagon to open.
|
||||
const fortyFooter = containerBooking('EXPORT-1', 1, 1);
|
||||
fortyFooter.bookingContainers![0]!.containerType = {
|
||||
code: '40GP',
|
||||
sizeFt: 40,
|
||||
} as never;
|
||||
const result = planWagonsWithStock({
|
||||
bookings: [
|
||||
containerBooking('EXPORT-1', 1, 1),
|
||||
containerBooking('INTERCITY-1', 1, 1),
|
||||
],
|
||||
bookings: [fortyFooter, containerBooking('INTERCITY-1', 1, 1)],
|
||||
allowed,
|
||||
stock: {
|
||||
mode: 'TRAIN',
|
||||
@@ -239,7 +244,6 @@ describe('planWagonsWithStock — leg-aware stock (intercity ride-along)', () =>
|
||||
codesByTypeId: new Map([[nw6.id, nw6.code]]),
|
||||
},
|
||||
legs: legs([
|
||||
// Both ride edge 0 — they compete for the one wagon.
|
||||
['EXPORT-1', { from: 0, to: 2 }],
|
||||
['INTERCITY-1', { from: 0, to: 1 }],
|
||||
]),
|
||||
@@ -252,9 +256,9 @@ describe('planWagonsWithStock — leg-aware stock (intercity ride-along)', () =>
|
||||
expect(result.deferred[0]!.reason).toContain('Train has no free NW6 wagon left');
|
||||
});
|
||||
|
||||
it('never packs bookings with different legs into the same wagon slot', () => {
|
||||
// Two 20ft units with room to share one wagon by TEU — but disjoint legs
|
||||
// must open separate slots (each with its own leg), not one mixed slot.
|
||||
it('packs disjoint-leg 20fts onto one wagon instead of appending a second', () => {
|
||||
// Two 20ft units, two wagons in stock — cross-leg TEU sharing still fills
|
||||
// the open wagon (span grows to the union) rather than opening wagon #2.
|
||||
const result = planWagonsWithStock({
|
||||
bookings: [
|
||||
containerBooking('EXPORT-1', 1, 1),
|
||||
@@ -273,11 +277,12 @@ describe('planWagonsWithStock — leg-aware stock (intercity ride-along)', () =>
|
||||
edgeCount: 2,
|
||||
});
|
||||
|
||||
expect(result.plan).toHaveLength(2);
|
||||
const bookingsPerSlot = result.plan.map((s) =>
|
||||
[...new Set(s.allocations.map((a) => a.bookingId))].sort(),
|
||||
);
|
||||
expect(bookingsPerSlot).toEqual([['EXPORT-1'], ['INTERCITY-1']]);
|
||||
expect(result.deferred).toHaveLength(0);
|
||||
expect(result.plan).toHaveLength(1);
|
||||
const bookingsInSlot = [
|
||||
...new Set(result.plan[0]!.allocations.map((a) => a.bookingId)),
|
||||
].sort();
|
||||
expect(bookingsInSlot).toEqual(['EXPORT-1', 'INTERCITY-1']);
|
||||
});
|
||||
|
||||
it('behaves exactly like the whole-route planner when no legs are given', () => {
|
||||
|
||||
Reference in New Issue
Block a user