mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 23:40:56 +00:00
eims integration master test complete
This commit is contained in:
@@ -129,6 +129,7 @@ import {
|
||||
perEdgeConsistUsage,
|
||||
validateContainerPlacements,
|
||||
validateMixedTrainLimitsPerEdge,
|
||||
MAX_TEU_SLOTS_PER_WAGON,
|
||||
type ContainerPlacementInput,
|
||||
type WagonPlanSlot,
|
||||
} from '../utils/wagon-plan.util';
|
||||
@@ -2930,6 +2931,10 @@ export class TrainSchedulingService {
|
||||
// dispatch pre-check keeps reporting these bookings as unloaded).
|
||||
const wagonAssignedIds = await this.getWagonAssignedBookingIds(scheduleId);
|
||||
if (wagonAssignedIds.size) {
|
||||
// Re-check the 1×40ft/2×20ft-per-wagon packing rule right before loading
|
||||
// is confirmed — allocation time already enforces it, but a wagon swap or
|
||||
// an edited allocation since then could have broken it unnoticed.
|
||||
await this.assertWagonContainerCapacity(scheduleId);
|
||||
// Export cargo must be received at the warehouse with a GRN before it can
|
||||
// be confirmed loaded — an allocation is not proof the goods are in hand.
|
||||
if (this.isExportSchedule(schedule)) {
|
||||
@@ -3397,6 +3402,72 @@ export class TrainSchedulingService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-check the 1×40ft / 2×20ft-per-wagon packing rule at loading confirmation
|
||||
* time. `validateContainerPlacements` already enforces this the moment a
|
||||
* booking is allocated to a wagon, but nothing re-checks it afterwards — a
|
||||
* wagon swap, an edited allocation, or a container item added out-of-band
|
||||
* between allocation and loading could still leave a wagon over its 2-TEU
|
||||
* capacity undetected until the train is already loaded. This closes that gap
|
||||
* by summing the TEU actually persisted per wagon (40ft = 2 TEU, 20ft = 1 TEU,
|
||||
* same size resolution as the marshalling document) right before loading is
|
||||
* confirmed.
|
||||
*/
|
||||
private async assertWagonContainerCapacity(scheduleId: string): Promise<void> {
|
||||
const rows: Array<{ sequenceNo: number; wagonNumber: string | null; teuUsed: string }> =
|
||||
await this.dataSource.query(
|
||||
`SELECT tsw.sequence_no AS "sequenceNo",
|
||||
pw.wagon_number AS "wagonNumber",
|
||||
SUM(
|
||||
CASE COALESCE(ct.size_ft, bct.size_ft)
|
||||
WHEN 40 THEN 2
|
||||
WHEN 20 THEN 1
|
||||
ELSE
|
||||
CASE
|
||||
WHEN bc.container_size ILIKE '%40%' THEN 2
|
||||
WHEN bc.container_size ILIKE '%20%' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
END
|
||||
) AS "teuUsed"
|
||||
FROM freight.train_set_wagons tsw
|
||||
JOIN freight.wagon_booking_allocations wba
|
||||
ON wba.train_set_wagon_id = tsw.id AND wba.deleted_at IS NULL
|
||||
JOIN freight.wagon_allocation_container_items wci
|
||||
ON wci.wagon_booking_allocation_id = wba.id AND wci.deleted_at IS NULL
|
||||
LEFT JOIN freight.container_types ct ON ct.id = wci.container_type_id
|
||||
LEFT JOIN freight.booking_container bc ON bc.id = wci.booking_container_id
|
||||
LEFT JOIN freight.container_types bct ON bct.id = bc.container_type_id
|
||||
LEFT JOIN freight.wagons pw ON pw.id = tsw.physical_wagon_id
|
||||
WHERE tsw.train_set_id = (
|
||||
SELECT train_set_id FROM freight.train_schedules WHERE id = $1
|
||||
)
|
||||
AND tsw.deleted_at IS NULL
|
||||
GROUP BY tsw.id, tsw.sequence_no, pw.wagon_number
|
||||
HAVING SUM(
|
||||
CASE COALESCE(ct.size_ft, bct.size_ft)
|
||||
WHEN 40 THEN 2
|
||||
WHEN 20 THEN 1
|
||||
ELSE
|
||||
CASE
|
||||
WHEN bc.container_size ILIKE '%40%' THEN 2
|
||||
WHEN bc.container_size ILIKE '%20%' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
END
|
||||
) > $2`,
|
||||
[scheduleId, MAX_TEU_SLOTS_PER_WAGON],
|
||||
);
|
||||
if (rows.length) {
|
||||
const labels = rows
|
||||
.map((r) => `wagon #${r.sequenceNo}${r.wagonNumber ? ` (${r.wagonNumber})` : ''}`)
|
||||
.join(', ');
|
||||
throw new BadRequestException(
|
||||
`These wagons exceed capacity (max 1×40ft or 2×20ft per wagon) — fix the container placement before confirming loading: ${labels}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private buildImportLoadListHtml(loadList: Awaited<ReturnType<TrainSchedulingService['generateImportLoadList']>>): string {
|
||||
const esc = (value: unknown) =>
|
||||
String(value ?? '-')
|
||||
|
||||
Reference in New Issue
Block a user