mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
fix(warehouses): truck-arrival crash on duplicate containers; auto-GRN on import unload
Truck Arrival died with "[@mantine/core] Duplicate options are not supported" when a booking carried the same container number on two container lines (the unit uniqueness is per line, not per booking): - bookingContainerWeights now GROUPs BY container number (MAX weight) - the weighing modal dedupes both the containers MultiSelect and the assigned-trucks Select defensively, so bad data degrades instead of crashing the whole page Import GRN is now issued automatically when the arrived train is unloaded: autoUnloadArrivedBookings stamps GRN-IMPORT-<date>-<booking> on both the create and update paths (never overwrites an existing GRN). The per-row GRN button in the import queue lights up with no extra clicks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1597,6 +1597,8 @@ export class WarehouseInventoryService {
|
|||||||
status: 'UNLOADED',
|
status: 'UNLOADED',
|
||||||
unloadedAt: now,
|
unloadedAt: now,
|
||||||
arrivedAt: existing.arrivedAt ?? now,
|
arrivedAt: existing.arrivedAt ?? now,
|
||||||
|
// Import GRN is issued automatically at train unload.
|
||||||
|
...(existing.grnNumber ? {} : { grnNumber: this.generateGrnNumber('IMPORT', booking.id, now) }),
|
||||||
});
|
});
|
||||||
await this.activityLog.record({
|
await this.activityLog.record({
|
||||||
activityType: 'INVENTORY_UNLOADED',
|
activityType: 'INVENTORY_UNLOADED',
|
||||||
@@ -1630,6 +1632,7 @@ export class WarehouseInventoryService {
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
weight: Number(booking.weight) || 0,
|
weight: Number(booking.weight) || 0,
|
||||||
status: 'UNLOADED',
|
status: 'UNLOADED',
|
||||||
|
grnNumber: this.generateGrnNumber('IMPORT', booking.id, now),
|
||||||
arrivedAt: now,
|
arrivedAt: now,
|
||||||
unloadedAt: now,
|
unloadedAt: now,
|
||||||
notes: allocated?.rule ? `Unloaded → ${allocated.path}` : 'Unloaded from arrived import train',
|
notes: allocated?.rule ? `Unloaded → ${allocated.path}` : 'Unloaded from arrived import train',
|
||||||
@@ -2757,11 +2760,12 @@ export class WarehouseInventoryService {
|
|||||||
const rows: Array<{ containerNumber: string; weightTons: string }> =
|
const rows: Array<{ containerNumber: string; weightTons: string }> =
|
||||||
await this.dataSource.query(
|
await this.dataSource.query(
|
||||||
`SELECT bcu.container_number AS "containerNumber",
|
`SELECT bcu.container_number AS "containerNumber",
|
||||||
COALESCE(bcu.vgm_tons, 0) AS "weightTons"
|
MAX(COALESCE(bcu.vgm_tons, 0)) AS "weightTons"
|
||||||
FROM freight.booking_container_units bcu
|
FROM freight.booking_container_units bcu
|
||||||
JOIN freight.booking_container bc
|
JOIN freight.booking_container bc
|
||||||
ON bc.id = bcu.booking_container_id AND bc.deleted_at IS NULL
|
ON bc.id = bcu.booking_container_id AND bc.deleted_at IS NULL
|
||||||
WHERE bc.booking_id = $1 AND bcu.deleted_at IS NULL
|
WHERE bc.booking_id = $1 AND bcu.deleted_at IS NULL
|
||||||
|
GROUP BY bcu.container_number
|
||||||
ORDER BY bcu.container_number`,
|
ORDER BY bcu.container_number`,
|
||||||
[bookingId],
|
[bookingId],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -202,8 +202,11 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
];
|
];
|
||||||
// Only trucks actually assigned to THIS booking (last-mile prefill or customer
|
// Only trucks actually assigned to THIS booking (last-mile prefill or customer
|
||||||
// portal) are selectable. No global fleet list — if nothing is assigned, the
|
// portal) are selectable. No global fleet list — if nothing is assigned, the
|
||||||
// operator types the plate manually in the field below.
|
// operator types the plate manually in the field below. Deduped by plate:
|
||||||
const truckSelectOptions = assignedTruckOptions;
|
// duplicate option values crash Mantine's Select.
|
||||||
|
const truckSelectOptions = [
|
||||||
|
...new Map(assignedTruckOptions.map((t) => [t.value, t])).values(),
|
||||||
|
];
|
||||||
// Neither a last-mile truck nor a customer truck has been assigned yet.
|
// Neither a last-mile truck nor a customer truck has been assigned yet.
|
||||||
const noTruckAssigned = assignedTruckOptions.length === 0 && !isCustomerAssignedTruck;
|
const noTruckAssigned = assignedTruckOptions.length === 0 && !isCustomerAssignedTruck;
|
||||||
const isTruckIdentityLocked = isEntranceLocked || isCustomerAssignedTruck || hasLastMileTruckPrefill;
|
const isTruckIdentityLocked = isEntranceLocked || isCustomerAssignedTruck || hasLastMileTruckPrefill;
|
||||||
@@ -216,10 +219,19 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
const containerWeightByNumber = new Map(
|
const containerWeightByNumber = new Map(
|
||||||
containerWeights.map((c) => [c.containerNumber.toUpperCase(), Number(c.weightTons) || 0]),
|
containerWeights.map((c) => [c.containerNumber.toUpperCase(), Number(c.weightTons) || 0]),
|
||||||
);
|
);
|
||||||
const containerSelectData = containerWeights.map((c) => ({
|
// Mantine Selects throw on duplicate option values — legacy bookings can carry
|
||||||
value: c.containerNumber,
|
// the same container number on two lines, so dedupe defensively.
|
||||||
label: `${c.containerNumber} · ${(Number(c.weightTons) || 0).toLocaleString()} t`,
|
const containerSelectData = [
|
||||||
}));
|
...new Map(
|
||||||
|
containerWeights.map((c) => [
|
||||||
|
c.containerNumber,
|
||||||
|
{
|
||||||
|
value: c.containerNumber,
|
||||||
|
label: `${c.containerNumber} · ${(Number(c.weightTons) || 0).toLocaleString()} t`,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).values(),
|
||||||
|
];
|
||||||
const selectedContainerNumbers = containerNumbers.map((n) => n.trim()).filter(Boolean);
|
const selectedContainerNumbers = containerNumbers.map((n) => n.trim()).filter(Boolean);
|
||||||
const selectedCargoWeight = Number(
|
const selectedCargoWeight = Number(
|
||||||
selectedContainerNumbers
|
selectedContainerNumbers
|
||||||
|
|||||||
Reference in New Issue
Block a user