Approve delivery now tracks the handover lifecycle exactly: the button appears
(detail page + portal dashboard) the moment a handover is generated and
disappears when the customer signs. The bookings list attaches the
handoverAwaitingSignature flag via one batched query per page; status
heuristics (COMPLETED / TRUCK_ASSIGNED+arrived) are gone.
- generate the arrival handover for ANY self-haul truck: portal-assigned OR
walk-in registered at the gate (isSelfHaulBooking: assigned_at set, or no
EDR last-mile leg). Same rule now guards the exit paper.
- remind every 5 minutes (in-app + SMS + email) until the handover is signed
(@Cron in HandoverService; one reminder per booking per tick)
- Truck Leaving no longer opens blank: the import queue mapper now carries
inv.notes, so the saved arrival renders read-only with only gate-out time
and gross weight editable
- container bookings get "Weigh truck? Yes/No": No skips tare/gross and the
container weight match (weighingSkipped on ReleaseOrderDto, decision made at
arrival sticks for the exit via the Weighing: SKIPPED note). Bulk always
weighs, unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blob downloads set responseType: 'blob', so axios delivers the JSON error body
as a Blob. extractErrorMessage() reads `.message` off it, finds nothing, and
falls back to "Request failed with status code 400" - hiding every real reason
("Handover must be signed...", "...must be fully paid before terminal release").
Only ContainerItemsModal used the async Blob decoder. Switch the remaining nine
blob-download catches to extractDownloadErrorMessage():
InventoryWorkbench release paper, handover
WarehouseInventoryTable GRN
ReceiveInventoryModal GRN (x2), handover, exit paper
FeePreviewModal release paper
TruckDispatchModal truck exit paper
Mutation-error catches are untouched - their bodies are already parsed JSON.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
READY_FOR_PICKUP allows STORED (park an import item back into storage), but
STORED allowed only RESERVED / READY_FOR_LOADING - so readyForPickup() hit
assertTransition(STORED, READY_FOR_PICKUP) and threw. The item could never
return to pickup, and getNextInventoryAction returned null for an import STORED
item, leaving the row with no action at all.
- allow STORED -> READY_FOR_PICKUP
- an inspected import STORED item now advances to ready-for-pickup
readyForPickup() still rejects non-IMPORT inventory, so the new edge cannot be
reached from the export flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AddGrnNumberToWarehouseInventory1828000000000 is recorded in the migrations
table but the column is absent - it was added, then dropped out-of-band. Because
TypeORM has the original recorded it will never re-run, so every GRN read/write
fails with "column grn_number does not exist":
- bulkReceive() INSERT names grn_number (receive to warehouse)
- importQueueByStatuses() Unloaded + Dispatch queues
- exportInventoryByStatus() Received / Ready-To-Load / Loaded tabs
- grnDocument() GRN PDF
Re-adds the column, backfills from the "GRN Number:" receive note, recreates the
partial index. Idempotent, and down() is a deliberate no-op so reverting the
repair cannot re-introduce the outage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added default capacities for container and bulk wagons.
- Updated wagonsFor method to consider weight and length for wagon calculations.
- Improved handling of overweight bookings with appropriate warnings.
- Adjusted tests to reflect changes in wagon capacity and validation logic.