Marshalling document empty wagon rendering

This commit is contained in:
Hagernesh
2026-07-17 14:20:34 +00:00
parent eb998bfddd
commit c0fdffa7ef
2 changed files with 191 additions and 34 deletions

View File

@@ -965,4 +965,120 @@ describe('TrainSchedulingService', () => {
expect(result).toHaveLength(2);
});
});
describe('marshalling documents', () => {
// Staff check these against the physical consist, so every wagon on the
// train set has to appear — an empty wagon that renders no row reads as a
// wagon that is not on the train.
const makeWagon = (sequenceNo: number, wagonNumber: string, allocations: unknown[]) => ({
sequenceNo,
wagonNumber,
physicalWagon: { wagonNumber },
wagonType: { code: 'NW5', name: 'Flat Wagon', tareWeightTons: 22 },
lengthMeters: 14,
capacityTons: 70,
allocations,
});
const loadedAllocation = {
bookingId: 'booking-1',
bookingReference: 'BK-2026-000001',
loadType: 'CONTAINER',
allocatedWeightTons: 24.5,
containerNumbers: ['CONT-001'],
booking: { id: 'booking-1', reference: 'BK-2026-000001', companyId: 'company-1' },
containerItems: [{ containerNumber: 'CONT-001', sealNumber: 'SEAL-1', chassisNumber: 'CH-1' }],
};
const countRows = (html: string) => (html.match(/<tr(?: class="empty")?>\s*<td/g) ?? []).length;
it('lists an empty wagon on the export document and marks it EMPTY', () => {
const schedule = {
id: 'schedule-1',
trainNumber: '8302',
direction: 'EXPORT',
trainSet: {
wagons: [
makeWagon(1, 'W-001', [loadedAllocation]),
makeWagon(2, 'W-002', []),
makeWagon(3, 'W-003', []),
],
},
scheduleBookings: [],
};
const html = (service as never as {
buildExportLoadListHtml: (s: unknown) => string;
}).buildExportLoadListHtml(schedule);
expect(countRows(html)).toBe(3);
expect(html).toContain('W-002');
expect(html).toContain('W-003');
expect(html.match(/EMPTY — no cargo allocated/g)).toHaveLength(2);
// The wagon count must agree with the rows the reader can see.
expect(html).toContain('3 (2 empty)');
});
it('lists an empty wagon on the import document and marks it EMPTY', () => {
const loadList = {
generatedAt: '2026-07-17T08:00:00.000Z',
trainScheduleId: 'schedule-1',
trainNumber: '8002',
route: 'Djibouti → Indode',
origin: 'Djibouti Port',
destination: 'Indode',
totalBookings: 1,
wagons: [
{ sequenceNo: 1, wagonNumber: 'W-001', allocations: [loadedAllocation] },
{ sequenceNo: 2, wagonNumber: 'W-002', allocations: [] },
],
operation: { status: {} },
};
const html = (service as never as {
buildImportLoadListHtml: (l: unknown) => string;
}).buildImportLoadListHtml(loadList);
expect(countRows(html)).toBe(2);
expect(html).toContain('W-002');
expect(html.match(/EMPTY — no cargo allocated/g)).toHaveLength(1);
expect(html).toContain('2 (1 empty)');
});
it('renders wagons in consist order regardless of the order the relation returns', () => {
const schedule = {
id: 'schedule-1',
trainNumber: '8302',
direction: 'EXPORT',
trainSet: {
wagons: [makeWagon(3, 'W-003', []), makeWagon(1, 'W-001', []), makeWagon(2, 'W-002', [])],
},
scheduleBookings: [],
};
const html = (service as never as {
buildExportLoadListHtml: (s: unknown) => string;
}).buildExportLoadListHtml(schedule);
expect(html.indexOf('W-001')).toBeLessThan(html.indexOf('W-002'));
expect(html.indexOf('W-002')).toBeLessThan(html.indexOf('W-003'));
});
it('omits the empty-count suffix when every wagon is loaded', () => {
const schedule = {
id: 'schedule-1',
trainNumber: '8302',
direction: 'EXPORT',
trainSet: { wagons: [makeWagon(1, 'W-001', [loadedAllocation])] },
scheduleBookings: [],
};
const html = (service as never as {
buildExportLoadListHtml: (s: unknown) => string;
}).buildExportLoadListHtml(schedule);
expect(html).not.toContain('empty)');
expect(html).not.toContain('EMPTY');
});
});
});