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 d6e778216..05f216a7b 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
@@ -1134,7 +1134,7 @@ describe('TrainSchedulingService', () => {
expect(html).toContain('2 (1 empty)');
});
- it('marks a leg slot on the import document as TO BE LOADED and keeps it out of the loaded tallies', () => {
+ it('drops a leg slot entirely from the import document — not part of the departing consist', () => {
const loadList = {
generatedAt: '2026-07-17T08:00:00.000Z',
trainScheduleId: 'schedule-1',
@@ -1176,15 +1176,16 @@ describe('TrainSchedulingService', () => {
buildImportLoadListHtml: (l: unknown) => string;
}).buildImportLoadListHtml(loadList);
- expect(html).toContain('TO BE LOADED AT DIRE DAWA PORT');
- // Departure station of the leg slot is its board yard, not the origin.
- expect(html).toContain('
();
@@ -3916,7 +3921,6 @@ export class TrainSchedulingService {
`,
];
}
- const pendingAt = opts?.pendingBoardYardLabelBySlot?.get(wagon.id);
return allocations.map((allocation) => {
const booking = allocation.booking ?? bookingById.get(allocation.bookingId);
const cargoType = (booking as unknown as { cargoType?: { name?: string; code?: string } } | undefined)?.cargoType;
@@ -3928,7 +3932,7 @@ export class TrainSchedulingService {
const chassisNumbers = containerItems.map((item) => item.chassisNumber).filter(Boolean).join(', ');
return `
${wagonCells}
- | ${pendingAt ? `TO LOAD AT ${esc(pendingAt).toUpperCase()} — ` : ''}${esc(cargoType?.name ?? cargoType?.code ?? allocation.loadType)} |
+ ${esc(cargoType?.name ?? cargoType?.code ?? allocation.loadType)} |
${esc(companyName)} |
${esc(containerNumbers || firstContainer?.containerNumber)} |
${esc(chassisNumbers)} |
@@ -3965,27 +3969,20 @@ export class TrainSchedulingService {
(wagon.allocations ?? []).length === 0 &&
!emptiesByWagon.get(Number(wagon.sequenceNo))?.length,
).length;
- const loadsHere = (wagon: TrainSetWagon) => !opts?.pendingBoardYardLabelBySlot?.get(wagon.id);
const totalWeight = wagons.reduce(
(sum, wagon) =>
- sum +
- (loadsHere(wagon)
- ? (wagon.allocations ?? []).reduce((wagonSum, allocation) => wagonSum + Number(allocation.allocatedWeightTons || 0), 0)
- : 0),
+ sum + (wagon.allocations ?? []).reduce((wagonSum, allocation) => wagonSum + Number(allocation.allocatedWeightTons || 0), 0),
0,
);
// Container count summary (40ft, 20ft) — empties returning to Djibouti are
- // physically on the train, so they count, and are called out on their own tile.
- // Cargo boarding downstream is not on this train yet — it tallies separately.
- let count40ft = 0, count20ft = 0, pendingContainers = 0;
+ // physically on the train, so they count, and are called out on their own
+ // tile. Cargo boarding downstream never enters this loop — `wagons` above
+ // already excludes those slots.
+ let count40ft = 0, count20ft = 0;
wagons.forEach((wagon) => {
(wagon.allocations ?? []).forEach((allocation) => {
(allocation.containerItems ?? []).forEach((item) => {
- if (!loadsHere(wagon)) {
- pendingContainers++;
- return;
- }
const size = this.resolveContainerItemSize(item);
if (size === 40) count40ft++;
else if (size === 20) count20ft++;
@@ -4053,7 +4050,6 @@ export class TrainSchedulingService {
Containers 40ft${esc(count40ft)}
Containers 20ft${esc(count20ft)}
Total containers${esc(count40ft + count20ft)}
- ${pendingContainers ? `To load en route${esc(pendingContainers)} containers
` : ''}
${emptyContainers.length ? `Empty containers${esc(emptyContainers.length)}
` : ''}
Prepared person${esc(schedule.preparedByUserId)}
Check person${esc(schedule.checkedByUserId)}
@@ -4253,30 +4249,23 @@ export class TrainSchedulingService {
.replace(/'/g, ''');
const date = (value: unknown) => (value ? new Date(value as string | Date).toLocaleString('en-GB') : '-');
const status = loadList.operation.status;
- // A leg slot (boardYard set) couples mid-corridor — its cargo is NOT on the
- // physical train this Djibouti-side document is checked against, so it must
- // stay out of the loaded tallies or the gate count stops matching.
- const loadsHere = (wagon: (typeof loadList.wagons)[number]) => !wagon.boardYard;
- const totalAllocations = loadList.wagons.reduce((sum, wagon) => sum + wagon.allocations.length, 0);
- const totalWeight = loadList.wagons.reduce(
- (sum, wagon) =>
- sum +
- (loadsHere(wagon)
- ? wagon.allocations.reduce((wagonSum, allocation) => wagonSum + Number(allocation.allocatedWeightTons || 0), 0)
- : 0),
+ // A leg slot (boardYard set) couples mid-corridor — it is not part of the
+ // consist this Djibouti-side document is checked against yet, so it gets
+ // no row and no count here at all. Its own coupling shows up on THAT
+ // stop's own marshalling document once it actually happens.
+ const wagons = loadList.wagons.filter((wagon) => !wagon.boardYard);
+ const totalAllocations = wagons.reduce((sum, wagon) => sum + wagon.allocations.length, 0);
+ const totalWeight = wagons.reduce(
+ (sum, wagon) => sum + wagon.allocations.reduce((wagonSum, allocation) => wagonSum + Number(allocation.allocatedWeightTons || 0), 0),
0,
);
- const emptyWagons = loadList.wagons.filter((wagon) => wagon.allocations.length === 0).length;
+ const emptyWagons = wagons.filter((wagon) => wagon.allocations.length === 0).length;
- // Container count summary (40ft, 20ft) — loaded at origin vs. en route
- let count40ft = 0, count20ft = 0, pendingContainers = 0;
- loadList.wagons.forEach((wagon) => {
+ // Container count summary (40ft, 20ft)
+ let count40ft = 0, count20ft = 0;
+ wagons.forEach((wagon) => {
wagon.allocations.forEach((allocation) => {
(allocation.containerItems ?? []).forEach((item) => {
- if (!loadsHere(wagon)) {
- pendingContainers++;
- return;
- }
const size = this.resolveContainerItemSize(item);
if (size === 40) count40ft++;
else if (size === 20) count20ft++;
@@ -4284,7 +4273,7 @@ export class TrainSchedulingService {
});
});
- const allocationRows = loadList.wagons
+ const allocationRows = wagons
.flatMap((wagon) => {
const wagonCells = `${esc(wagon.sequenceNo)} |
${esc(wagon.wagonNumber)} |
@@ -4317,7 +4306,7 @@ export class TrainSchedulingService {
${esc(allocation.loadType)} |
${esc(allocation.containerNumbers.length ? allocation.containerNumbers.join(', ') : '-')} |
${esc(sealNumbers || '-')} |
- ${wagon.boardYard ? `TO BE LOADED AT ${esc(wagon.boardYard).toUpperCase()}` : ''} |
+ |
${esc(Number(allocation.allocatedWeightTons || 0).toFixed(3))} |
`;
},
@@ -4384,13 +4373,12 @@ export class TrainSchedulingService {
Origin${esc(loadList.origin)}
Destination${esc(loadList.destination)}
Total bookings${esc(loadList.totalBookings)}
- Wagons${esc(loadList.wagons.length)}${emptyWagons ? ` (${emptyWagons} empty)` : ''}
+ Wagons${esc(wagons.length)}${emptyWagons ? ` (${emptyWagons} empty)` : ''}
Allocations${esc(totalAllocations)}
Total weight${esc(totalWeight.toFixed(3))} T
Containers 40ft${esc(count40ft)}
Containers 20ft${esc(count20ft)}
Total containers${esc(count40ft + count20ft)}
- ${pendingContainers ? `To load en route${esc(pendingContainers)} containers
` : ''}
Gatepass granted${esc(date(loadList.operation.gatepassGrantedAt))}