Merge pull request #1419 from Tria-plc/credit-invoice

feat(train-scheduling): show coupled/uncoupled wagons on intercity ma…
This commit is contained in:
Hagernesh Tadesse
2026-08-26 22:06:26 +03:00
committed by GitHub
2 changed files with 59 additions and 0 deletions

View File

@@ -1209,6 +1209,32 @@ describe('TrainSchedulingService', () => {
expect(html).toContain('<span>To load en route</span><strong>1 containers</strong>');
});
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',

View File

@@ -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<string, string>;
// 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 ? `<div class="tile"><span>Current position</span><strong>${esc(opts.positionLabel)}</strong></div>` : ''}
</div>
${
opts?.consistChangesAtStop?.length
? `<div class="notice">
<b>Consist changed at this stop:</b>
${(() => {
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(' &nbsp;|&nbsp; ');
})()}
</div>`
: ''
}
<table>
<thead>
<tr>