Frontend for the per-truck EDR backend:
- Assign modal takes a container LOAD per truck (MultiSelect, max 2) instead of a
single container, so a truck can carry one 40ft or two 20ft. Sends
containerNumbers[]; the API enforces the size rule.
- requiredVehicles is now size-aware: a 40ft fills a truck (1 each), two 20ft
share one. It previously assumed ceil(containers / 2) regardless of size, so a
2x40ft booking under-counted the trucks needed.
- Bulk bookings show the drawdown in the assign modal — remaining vs total
tonnage, what's been hauled, and a "fully hauled" state when it reaches 0.
- New "Truck exit papers" row action opens a per-truck list (plate, containers,
arrival, exit, net weight) with a per-truck exit-paper download.
- last-mile findAll/findById now load each assignment's containers so the UI can
hydrate a truck's load.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EDR last-mile is multi-truck but was modelled as one: setVehicles accepted any
number of trucks with no validation, arrival/delivery were stamped once per
last_mile record (N trucks shared one timestamp), EDR trucks got no exit paper,
and the per-truck EDR handover never happened because deliver() resolved the
plate via last_mile_container_allocations — a table nothing writes.
- Migration 2260000000000: per-truck arrived_at/departed_at/gross_weight_tons/
net_weight_tons on last_mile_vehicle_assignments, plus a
last_mile_vehicle_containers child table (a truck holds 1x40ft OR 2x20ft, so
the single container_number scalar could not express a load). Weights are
TONNES and named accordingly — the older gross_weight_kg lies about its unit.
- setVehicles: enforce the size rule (one 40ft, or two 20ft), container
membership, one-container-one-truck, and never more trucks than containers.
Bulk carries no containers and is instead gated on tonnage remaining.
- Bulk drawdown: remainingTonsForBooking = booking VGM minus the net weighed off
every departed truck (both tonnes, no conversion), exposed as
GET /last-mile/booking/:bookingId/remaining-tons.
- release() now stamps the EDR truck's own arrival and exit (matched by plate, so
it works for bulk too) alongside the existing customer-truck stamp. The exit
weighing itself is untouched.
- New GET /warehouse-inventory/edr-truck-exit-paper/:assignmentId — per-truck
exit paper for EDR trucks. Deliberately not signature-gated: EDR handovers are
generated at delivery, after the truck has left.
- deliver(): resolve the handover plate from the truck's own containers instead
of the dead allocations table, so EDR handovers are genuinely per-truck.
- Notify the customer (portal inbox + SMS/email) when a truck leaves — one hook
in release() covers both self-haul and EDR, since it is the single exit path.
Self-haul is intentionally unchanged (one booking-level handover signed once).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>