Files
edr-platform/e2e/freight/cypress/fixtures/seed-g2-weight.sql
Marshal 123f925cc1 add Group 9 delivery scenarios for self-haul and last-mile paths
- Implemented G9·S38 tests for customer self-haul truck assignments, ensuring compliance with container limits and truck assignments.
- Added G9·S39 tests for last-mile delivery, self-haul, and yard pickup, verifying independent paths for multiple bookings on the same train.
- Created seed data for Group 1 and Group 2 scenarios, ensuring proper setup for weight and capacity tests.
- Updated booking interface to deprecate  in favor of  for better clarity in allocations.
2026-08-01 12:14:37 +00:00

133 lines
5.8 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Arrange-data for the GROUP 2 weight/tolerance scenarios (flows/g2_weight.cy.ts).
-- Run AFTER seed-import-corridor.sql. Idempotent.
--
-- Group 2 is the only group where WEIGHT binds before slots. Group 1's train
-- (TRN-G1-1) deliberately keeps every non-slot axis slack; this one does the
-- opposite, so the two must never share rolling stock.
--
-- THE ARITHMETIC (all from train-capacity.util.ts)
--
-- The weight axis is GROSS — a locomotive hauls the wagon as well as its
-- cargo (`grossWagonWeightTons` = tare + cargo). NW5 tare is 22.4T, and two
-- 20ft containers ride one NW5. So with a heavy VGM of 28T per container:
--
-- wagon gross = 2 × 28 + 22.4 = 78.4T ← S9's number exactly
--
-- S9 base pull 3500T: 35 wagons × 78.4 = 2744.0T ✔ fits
-- 45 wagons × 78.4 = 3528.0T ✘ over base
-- → C is blocked on WEIGHT while 18 slots still sit empty.
--
-- S10 tolerance 90T → cap 3590T: 3528 ≤ 3590, so C is admitted WHOLE.
-- Tolerance is spendable ONLY to admit a booking whole.
--
-- S11 after 2744T used, base room = 3500 2744 = 756T.
-- 756 / 78.4 = 9.64 → a split may size at most 9 wagons (705.6T).
-- It may NEVER reach 10 by dipping into the 90T tolerance, because
-- sizePartialOfferWagons budgets against BASE only
-- (train-capacity.util.ts:58, booking-batch.service.ts:4109).
--
-- S12 light cargo (12T/container): wagon gross = 2 × 12 + 22.4 = 46.4T.
-- 53 × 46.4 = 2459.2T — only 70% of base. SLOTS bind, not weight.
--
-- TWO LOCOMOTIVE PAIRS, ONE TRAIN EACH
--
-- Pull weight ADDS UP across a set but length takes the MINIMUM
-- (combinedLocomotiveLimits, train-capacity.util.ts:358), so a pair of 1750T
-- locos gives exactly the 3500T base the scenarios assume. Weight tolerance
-- adds up too — hence 45T each for the 90T set.
--
-- TRN-G2-BASE LOCO-G2-A/B 1750T each, tolerance 0 → 3500T base, no slack
-- TRN-G2-TOL LOCO-G2-C/D 1750T each, tolerance 45T → 3500T base + 90T
--
-- Both carry 53 NW5 wagons so the SLOT axis is identical to Group 1 and any
-- difference in outcome is attributable to weight alone.
-- 1. Locomotives. Length 760m keeps the length axis slack (53 × 13.966 =
-- 740.2m < 760m), so only weight and slots can ever bind here.
INSERT INTO freight.locomotives
(id, code, max_pull_weight_tons, max_train_length_meters,
overage_tolerance_tons, overage_tolerance_meters, current_yard_id)
SELECT gen_random_uuid(), v.code, 1750, 760, v.tol, 0, y.id
FROM (VALUES
('LOCO-G2-A', 0), ('LOCO-G2-B', 0),
('LOCO-G2-C', 45), ('LOCO-G2-D', 45)
) AS v(code, tol)
JOIN freight.yards y ON y.code = 'DJIB_PORT'
WHERE NOT EXISTS (SELECT 1 FROM freight.locomotives l WHERE l.code = v.code);
-- Re-assert every seed: a prior run's row may predate these numbers, and the
-- whole group's arithmetic depends on them being exact.
UPDATE freight.locomotives l
SET max_pull_weight_tons = 1750,
max_train_length_meters = 760,
overage_tolerance_tons = v.tol,
overage_tolerance_meters = 0,
current_yard_id = (SELECT id FROM freight.yards WHERE code = 'DJIB_PORT')
FROM (VALUES
('LOCO-G2-A', 0), ('LOCO-G2-B', 0),
('LOCO-G2-C', 45), ('LOCO-G2-D', 45)
) AS v(code, tol)
WHERE l.code = v.code
AND (l.max_pull_weight_tons IS DISTINCT FROM 1750
OR l.max_train_length_meters IS DISTINCT FROM 760
OR l.overage_tolerance_tons IS DISTINCT FROM v.tol);
-- 2. The two trains at the corridor origin.
INSERT INTO freight.trains
(id, code, train_name, capacity_tons, current_yard_id,
import_train_number, export_train_number)
SELECT gen_random_uuid(), v.code, v.name, 3500, y.id, v.imp, v.exp
FROM (VALUES
('TRN-G2-BASE', 'E2E Group-2 Base-Weight Carrier', '9302', '9301'),
('TRN-G2-TOL', 'E2E Group-2 Tolerance Carrier', '9304', '9303')
) AS v(code, name, imp, exp)
JOIN freight.yards y ON y.code = 'DJIB_PORT'
WHERE NOT EXISTS (SELECT 1 FROM freight.trains t WHERE t.code = v.code);
-- 3. Couple each locomotive pair to its train.
INSERT INTO freight.train_locomotives (id, train_id, locomotive_id, sequence_no)
SELECT gen_random_uuid(), t.id, l.id, v.seq
FROM (VALUES
('TRN-G2-BASE', 'LOCO-G2-A', 0), ('TRN-G2-BASE', 'LOCO-G2-B', 1),
('TRN-G2-TOL', 'LOCO-G2-C', 0), ('TRN-G2-TOL', 'LOCO-G2-D', 1)
) AS v(train_code, loco_code, seq)
JOIN freight.trains t ON t.code = v.train_code
JOIN freight.locomotives l ON l.code = v.loco_code
WHERE NOT EXISTS (
SELECT 1 FROM freight.train_locomotives tl
WHERE tl.train_id = t.id AND tl.locomotive_id = l.id
);
-- 4. 53 dedicated NW5 wagons per train (WGN-G2B-*, WGN-G2T-*). Same slot count
-- as Group 1 on purpose — see the header.
INSERT INTO freight.wagons (id, wagon_number, wagon_type_id, current_yard_id)
SELECT gen_random_uuid(), v.prefix || lpad(g::text, 2, '0'), wt.id, y.id
FROM generate_series(1, 53) AS g
CROSS JOIN (VALUES ('WGN-G2B-'), ('WGN-G2T-')) AS v(prefix)
JOIN freight.wagon_types wt ON wt.code = 'NW5'
JOIN freight.yards y ON y.code = 'DJIB_PORT'
WHERE NOT EXISTS (
SELECT 1 FROM freight.wagons w
WHERE w.wagon_number = v.prefix || lpad(g::text, 2, '0')
);
-- Couple them, unconditionally re-asserted: a 52-wagon consist would shift the
-- weight-vs-slot boundary the whole group is built to probe.
UPDATE freight.wagons w
SET train_id = t.id,
sequence_number = g.seq,
status = 'ASSIGNED',
current_yard_id = t.current_yard_id
FROM freight.trains t,
LATERAL (
SELECT s AS seq,
CASE WHEN t.code = 'TRN-G2-BASE' THEN 'WGN-G2B-' ELSE 'WGN-G2T-' END
|| lpad(s::text, 2, '0') AS num
FROM generate_series(1, 53) AS s
) g
WHERE t.code IN ('TRN-G2-BASE', 'TRN-G2-TOL')
AND w.wagon_number = g.num
AND (w.train_id IS DISTINCT FROM t.id
OR w.sequence_number IS DISTINCT FROM g.seq
OR w.status IS DISTINCT FROM 'ASSIGNED');