diff --git a/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.spec.ts b/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.spec.ts index 35eed0f80..2b8971e81 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.spec.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.spec.ts @@ -1209,6 +1209,32 @@ describe('TrainSchedulingService', () => { expect(html).toContain('To load en route1 containers'); }); + it('prints coupled/switched wagons logged at this stop, and omits the box when there are none', () => { + const schedule = { + id: 'schedule-1', + trainNumber: '8302', + direction: 'EXPORT', + trainSet: { wagons: [{ ...makeWagon(1, 'W-001', [loadedAllocation]), id: 'slot-1' }] }, + scheduleBookings: [], + }; + const build = (service as never as { + buildExportLoadListHtml: (s: unknown, o?: unknown) => string; + }).buildExportLoadListHtml.bind(service); + + const withChanges = build(schedule, { + consistChangesAtStop: [ + { action: 'ADD', wagonNumber: 'W-1002' }, + { action: 'SWITCH', wagonNumber: 'W-0501 → W-1003' }, + ], + }); + expect(withChanges).toContain('Consist changed at this stop'); + expect(withChanges).toContain('Coupled: W-1002'); + expect(withChanges).toContain('Uncoupled — replaced: W-0501 → W-1003'); + + const withoutChanges = build(schedule, {}); + expect(withoutChanges).not.toContain('Consist changed at this stop'); + }); + it('lists loaded empty containers by number and states they are empty', () => { const schedule = { id: 'schedule-1', diff --git a/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.ts b/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.ts index ef5359207..100123238 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/services/train-scheduling.service.ts @@ -3558,6 +3558,16 @@ export class TrainSchedulingService { : `At ${schedule.originStation?.label ?? schedule.originStation?.code ?? 'origin'} — no checkpoint recorded`; const { wagons, unassignedBookings } = this.intercityOnBoardView(schedule); + // Couples/switches logged AT THIS STOP — what staff standing here actually + // just did to the consist. Bare trims (REMOVE, no replacement) are left + // out: nothing new to point staff at for those. Origin adjustments (a + // different yard) don't show up on this stop's document. + const consistChangesAtStop = last + ? await this.dataSource.getRepository(ScheduleWagonAdjustmentLog).find({ + where: { trainScheduleId: scheduleId, yardId: last.yardId, action: In(['ADD', 'SWITCH']) }, + order: { occurredAt: 'DESC' }, + }) + : []; const html = this.buildExportLoadListHtml(schedule, { title: 'Intercity Marshalling Document / Load List (Marshalling 2)', positionLabel, @@ -3565,6 +3575,7 @@ export class TrainSchedulingService { unassignedBookings, emptyContainers: await this.loadedEmptyContainers(scheduleId), logoImageUrl: await this.logoSettings.getLogoImageUrl(), + consistChangesAtStop, }); // Styled table-aware fallback (marshalling grid) — see importLoadListDocument. const buffer = await this.pdfDocuments.renderTabularDocument(html, 'Intercity marshalling / load list'); @@ -3634,6 +3645,10 @@ export class TrainSchedulingService { // Slots that couple to the train downstream (slot id → board yard label). // Their cargo renders as TO LOAD AT and stays out of the loaded tallies. pendingBoardYardLabelBySlot?: Map; + // Intercity (Marshalling 2) only: couples/switches logged at the stop + // this document is printed at (see ScheduleWagonAdjustmentLog). Origin + // import/export docs never pass this, so they render no such box. + consistChangesAtStop?: ScheduleWagonAdjustmentLog[]; }, ): string { const esc = (value: unknown) => @@ -3844,6 +3859,24 @@ export class TrainSchedulingService { ${opts?.positionLabel ? `
Current position${esc(opts.positionLabel)}
` : ''} + ${ + opts?.consistChangesAtStop?.length + ? `
+ Consist changed at this stop: + ${(() => { + const coupled = opts.consistChangesAtStop.filter((row) => row.action === 'ADD'); + const switched = opts.consistChangesAtStop.filter((row) => row.action === 'SWITCH'); + return [ + coupled.length ? `Coupled: ${esc(coupled.map((row) => row.wagonNumber).join(', '))}` : '', + switched.length ? `Uncoupled — replaced: ${esc(switched.map((row) => row.wagonNumber).join(', '))}` : '', + ] + .filter(Boolean) + .join('  |  '); + })()} +
` + : '' + } +