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>
Every booking row in the queue tables (export received, ready-to-load,
loaded/dispatch, import unloaded) gets a chevron that expands to the
booking's items: container number, goods, lifecycle stage badge
(PENDING..DELIVERED), carrying truck, and GRN.
Bulk bookings (no container units) show a one-line summary instead
("Bulk cargo — Coffee export cargo, 3,200 t"). The expansion reuses the
['container-items', bookingId] query the container modal already uses, so
data is shared and instant after either has loaded it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- statuses render through the shared InventoryStatusBadge (humanized label,
consistent per-status color) instead of raw enums in ad-hoc badges
- Ready-To-Load drops its selection checkboxes: nothing consumed the
selection since auto-load became train-scoped
- bulk toasts now say WHY items were skipped ("3 skipped — Booking not PAID")
via a shared skippedSummary helper
- one-click irreversible bulk actions (Dispatch All/Selected, Mark Selected
as Inspected x2, Auto Unload train) now ask for confirmation through a
small local ConfirmActionModal - no @mantine/modals dependency added.
Receive All stays unconfirmed: it already funnels through the
truck-entrance modal with an explicit Save.
- table minWidths retuned for the slimmed column sets
(1700->1350, 1600->1200, 2000->1650)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Queue tables showed "Booking ID" / "Customer ID" as dimmed 8-char UUID hashes
next to the human-readable Booking Ref and Customer Name - unusable columns
that forced extra horizontal scrolling. Removed across all six tables
(eligible, received, ready-to-load, loaded/dispatch, import unloaded, import
train detail).
The GRN document button also rendered twice per row (inside the Booking Ref
cell and again in the GRN column) in four tables - kept the GRN column only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added status and cargo filters to the ShipmentRequestsPage.
- Implemented date range filtering for preferred dates.
- Introduced sorting options for shipment requests based on submission date and reference.
- Enhanced the display of shipment request details, including status badges and customer information.
- Updated the UI to include a search input with clear functionality and improved layout for filters.
feat: add equipment return option in new shipment form
- Introduced a toggle for equipment return in the NewShipmentPage.
- Updated form schema to include field for container contracts.
- Enhanced user experience with visual feedback on the equipment return selection.
fix: update booking DTO to include equipment return option
- Added field to CreateBookingUnderContractDto for per-shipment override.
- Updated related types and schemas to accommodate the new field for better contract handling.
The modal read the booking id only from item.booking?.id, but inventory-
workbench rows carry bookingId without the booking relation. Result: the
container-weights and assigned-trucks queries never ran, so "Recorded net
weight (system)" fell back to the inventory row's weight (often 0), the
container MultiSelect never appeared, and the truck dropdown showed
"not assigned" even when trucks existed - while the server correctly computed
the cargo weight and rejected the mismatch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Auto Load Ready Items" now opens a train picker (pre-dispatch trains with
these bookings assigned, via the existing loadable-trains flow). No train
available -> no auto-loading, with a clear notice. Loading goes through the
existing per-wagon load path, so items without an allocated wagon are skipped
with a reason.
The train association is stored on the existing warehouse_loadings table
(no new table needed): new train_schedule_id column + a note recording train
number, origin -> destination, and departure time; wagon_id becomes nullable.
The trainless load-passed-export endpoint, its frontend wiring, and the unused
useLoadPassedExport hook are removed.
Migration 2100000000000 (idempotent) also applied to the dev database.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- remove Incoterms, HS codes, and Item code from the truck-entrance form:
nothing in the booking captures them, so they were always hand-typed noise
- remove the hand-typed "Warehouse code and location" input: the backend now
stamps it from the warehouse/yard/zone the operator actually selected
(WH / YARD / ZONE codes), so the GRN and notes always match reality
- Declaration number and Item description widen to full rows
Backend DTO keeps the optional fields for compatibility.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The receive form now arrives filled with everything the booking already knows,
instead of the operator re-typing it:
- container numbers come from the per-unit records (booking_container_units)
entered at booking time, falling back to the line-level aggregate
- customs seal number prefilled from the units' seal numbers
- net weight prefilled from the booking's declared cargo weight (tonnes)
- driver signatory defaults to the arriving driver's name
Existing prefills (owner, TIN, phone, booking ref, cargo, packaging, unit
count, first-mile / customer truck + driver) unchanged; all fields stay
editable where they were editable before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wagon loading happens in the train flow and dispatch at the train level, which
already advances inventory - the per-row buttons duplicated that and confused
operators.
- WarehouseInventoryTable (import dispatch queue + warehouse pages): suppress
the 'load'/'dispatch' next-action buttons and drop the extra per-row Dispatch
on READY_FOR_PICKUP rows (Store stays)
- Export Dispatch Queue: drop the per-row Dispatch button and its Actions
column; the bulk Dispatch All / Dispatch Selected controls remain
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
- Updated the logic in to ensure that a booking only earns its gate pass once the freight charges are settled.
- Added logging for bookings that have not settled freight payment when securing gate passes.
- Modified seeders to ensure that bookings have associated company profiles to prevent data inconsistencies.
- Updated freight permissions to include new clearance actions for bookings.
- Enhanced the UI to reflect changes in the clearance process, including new shipment request pages and improved status handling in the clearance action panel.
- Adjusted the contract clearance list to accommodate both customs contracts and shipment bookings.
- Improved the handling of GENERAL contracts in various components to ensure proper booking flow and visibility.
- Removed maxWagonsPerTrain from WagonType entity and related DTOs.
- Updated containerWagonsForLines function to calculate required wagons based on container lines more accurately.
- Added unit tests for containerWagonsForLines to ensure correct calculations.
- Adjusted related services and scripts to reflect the removal of maxWagonsPerTrain.
- Enhanced booking and contract components to use new status labels for better user experience.
- Implemented validation for unique container numbers in shipment forms.