mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 21:20:57 +00:00
fix(operations): prefill booking context on single assign; enforce per-truck container loads
- single-row Assign vehicle uses the full single-record flow (details + containers) - release() rejects exit containers not assigned to the departing truck - weighing modal offers only the selected truck's assigned containers
This commit is contained in:
@@ -3011,6 +3011,29 @@ export class WarehouseInventoryService {
|
||||
);
|
||||
}
|
||||
|
||||
// A truck leaves with the containers ASSIGNED to it — never another
|
||||
// truck's. Enforced whenever the truck has an assigned load on file
|
||||
// (customer self-haul or EDR last-mile).
|
||||
if (dto.containerNumber && dto.truckPlateNumber?.trim()) {
|
||||
const selectedNumbers = dto.containerNumber
|
||||
.split(/[,;\n]+/)
|
||||
.map((n) => n.trim().toUpperCase())
|
||||
.filter(Boolean);
|
||||
const assigned = await this.truckAssignedContainers(
|
||||
item.bookingId,
|
||||
dto.truckPlateNumber.trim(),
|
||||
);
|
||||
if (assigned.length && selectedNumbers.length) {
|
||||
const foreign = selectedNumbers.filter((n) => !assigned.includes(n));
|
||||
if (foreign.length) {
|
||||
throw new BadRequestException(
|
||||
`Container${foreign.length > 1 ? 's' : ''} ${foreign.join(', ')} ` +
|
||||
`not assigned to truck ${dto.truckPlateNumber.trim()} — each truck may only carry out its own assigned containers`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Authoritative weight match: the truck's net (gross − tare) must equal the
|
||||
// total VGM cargo weight of the containers selected as loaded on it.
|
||||
// Skipped when the operator chose not to weigh (containers only).
|
||||
@@ -3653,6 +3676,32 @@ export class WarehouseInventoryService {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Container numbers assigned to a truck (by plate) on this booking, from both
|
||||
* haulage paths: customer self-haul (customer_truck_containers) and EDR
|
||||
* last-mile (last_mile_vehicle_containers / legacy scalar). Uppercased.
|
||||
*/
|
||||
private async truckAssignedContainers(bookingId: string, plate: string): Promise<string[]> {
|
||||
const rows: Array<{ cn: string | null }> = await this.dataSource.query(
|
||||
`SELECT UPPER(cc.container_number) AS cn
|
||||
FROM freight.customer_truck_assignments a
|
||||
JOIN freight.customer_truck_containers cc
|
||||
ON cc.assignment_id = a.id AND cc.deleted_at IS NULL
|
||||
WHERE a.booking_id = $1 AND UPPER(a.plate_number) = UPPER($2) AND a.deleted_at IS NULL
|
||||
UNION
|
||||
SELECT UPPER(COALESCE(vc.container_number, va.container_number)) AS cn
|
||||
FROM freight.last_mile_vehicle_assignments va
|
||||
JOIN freight.last_mile l ON l.id = va.last_mile_id AND l.deleted_at IS NULL
|
||||
LEFT JOIN freight.last_mile_vehicle_containers vc
|
||||
ON vc.assignment_id = va.id AND vc.deleted_at IS NULL
|
||||
JOIN freight.vehicles v ON v.id = va.vehicle_id
|
||||
WHERE l.booking_id = $1 AND va.deleted_at IS NULL
|
||||
AND (UPPER(v.power_plate_no) = UPPER($2) OR UPPER(v.plate_number) = UPPER($2))`,
|
||||
[bookingId, plate],
|
||||
);
|
||||
return rows.map((r) => r.cn).filter((n): n is string => Boolean(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* The booking's containers with their VGM cargo weight (tonnes), keyed by
|
||||
* container number. Drives the truck-leaving exit weighing: the selected
|
||||
|
||||
Reference in New Issue
Block a user