mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 10:10:57 +00:00
fix(freight): correct direct CAS lines and sync inventory on schedule load
End-to-end testing of the direct truck-to-train flow surfaced two defects in the carriage acceptance sheet's no-wagon fallback. Container numbers were read from freight.containers, which only gains rows at allocation, so a direct booking's declared containers never appeared; they are read from booking_container_units now, seal numbers included. The weight used bulkTotalWeightTons alone, which is null for CONTAINER and PER_TON bulk bookings, printing 0.00 — cargoTotalWeightVgm is the real weight there and only holds an item count for PER_ITEM break-bulk. Separately, warehouse cargo can be loaded from the Load-to-Train queue or from the schedule, but loading from the schedule never advanced warehouse_inventory. The booking went IN_TRANSIT while its cargo still read as sitting in the warehouse — the same inconsistency that produced the spurious dispatch block. loadBooking now moves the inventory to LOADED in the same transaction, and no-ops for direct bookings.
This commit is contained in:
@@ -92,6 +92,7 @@ interface CarriageAcceptanceWagonRow {
|
||||
interface CarriageAcceptanceReceivedRow {
|
||||
allocatedWeightTons: string | null;
|
||||
containerNumbers: string | null;
|
||||
sealNumbers?: string | null;
|
||||
}
|
||||
|
||||
const URGENT_PRIORITY_THRESHOLD = 1000;
|
||||
@@ -291,15 +292,19 @@ export class BookingsService {
|
||||
if (pendingWagons) {
|
||||
// Direct truck-to-train cargo never enters the warehouse, so there is no
|
||||
// GRN'd inventory to build the sheet from. Choosing direct handover is
|
||||
// itself the acceptance, so the sheet issues off the booking's own
|
||||
// containers (or its VGM weight when the cargo is bulk).
|
||||
// itself the acceptance, so the sheet issues off the containers the
|
||||
// customer declared on the booking — freight.containers only gains rows at
|
||||
// allocation, by which point the wagon query above already serves.
|
||||
const receivedLines: CarriageAcceptanceReceivedRow[] = isDirectExport
|
||||
? await this.dataSource.query(
|
||||
`SELECT NULL::numeric AS "allocatedWeightTons",
|
||||
c.container_number AS "containerNumbers"
|
||||
FROM freight.containers c
|
||||
WHERE c.booking_id = $1 AND c.deleted_at IS NULL
|
||||
ORDER BY c.container_number`,
|
||||
unit.container_number AS "containerNumbers",
|
||||
unit.seal_number AS "sealNumbers"
|
||||
FROM freight.booking_container_units unit
|
||||
JOIN freight.booking_container line
|
||||
ON line.id = unit.booking_container_id AND line.deleted_at IS NULL
|
||||
WHERE line.booking_id = $1 AND unit.deleted_at IS NULL
|
||||
ORDER BY unit.container_number`,
|
||||
[bookingId],
|
||||
)
|
||||
: booking.tradeDirection === 'EXPORT'
|
||||
@@ -319,11 +324,13 @@ export class BookingsService {
|
||||
)
|
||||
: [];
|
||||
// Bulk direct cargo has no containers — one line carrying the booking's
|
||||
// declared weight still makes a valid sheet.
|
||||
// declared weight still makes a valid sheet. bulkTotalWeightTons only
|
||||
// holds the real tonnage for PER_ITEM break-bulk; everywhere else (PER_TON
|
||||
// bulk and every container booking) the VGM column is the weight.
|
||||
if (isDirectExport && receivedLines.length === 0) {
|
||||
const totalWeight = booking.bulkTotalWeightTons ?? booking.cargoTotalWeightVgm;
|
||||
receivedLines.push({
|
||||
allocatedWeightTons:
|
||||
booking.bulkTotalWeightTons == null ? null : String(booking.bulkTotalWeightTons),
|
||||
allocatedWeightTons: totalWeight == null ? null : String(totalWeight),
|
||||
containerNumbers: null,
|
||||
});
|
||||
}
|
||||
@@ -347,7 +354,7 @@ export class BookingsService {
|
||||
marshalledAt: null,
|
||||
arrivalAt: null,
|
||||
containerNumbers: row.containerNumbers,
|
||||
sealNumbers: null,
|
||||
sealNumbers: row.sealNumbers ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,20 @@ export class BookingJourneyService {
|
||||
await manager
|
||||
.getRepository(TrainScheduleBooking)
|
||||
.update({ trainScheduleId: scheduleId, bookingId }, { loadingStatus: 'LOADED' });
|
||||
// Warehouse cargo may be loaded either from the warehouse Load-to-Train
|
||||
// queue or from the schedule itself. Loading here must move its inventory
|
||||
// too, otherwise the goods read as still sitting in the shed while the
|
||||
// train leaves with them. No-ops for direct truck-to-train (no inventory).
|
||||
// ponytail: no WarehouseLoading record on this path — those are only read
|
||||
// back as per-inventory loading history, never billed. Create them here if
|
||||
// that history ever has to be complete.
|
||||
await manager.query(
|
||||
`UPDATE freight.warehouse_inventory
|
||||
SET status = 'LOADED', loaded_at = COALESCE(loaded_at, $2), updated_at = NOW()
|
||||
WHERE booking_id = $1 AND deleted_at IS NULL
|
||||
AND status NOT IN ('LOADED', 'DISPATCHED')`,
|
||||
[bookingId, now],
|
||||
);
|
||||
// The facility handed the cargo over — raise its GRN. No-ops for yards
|
||||
// without a facility (import/export terminals), which keep their own flow.
|
||||
await this.facilityHandling.recordHandling(manager, {
|
||||
|
||||
Reference in New Issue
Block a user