Commit Graph

53 Commits

Author SHA1 Message Date
hager
6ebb402131 feat(freight-backoffice): enter several trucks on one arrival
The receive drawer captured one truck per arrival, so a customer whose
containers came on three trucks had to be received three times over.

Stage trucks instead. "Add another truck" validates the form exactly as a
single-truck receive does, files the truck with its own plate, driver and
containers, and clears the form for the next one. The staged trucks are
listed above the form with their boxes, each removable, and the arrival is
received in one operation.

Containers already staged on an earlier truck drop out of the picker, so
the same box cannot be sent twice.

Receiving with a part-filled form and trucks already staged is treated as
the operator still typing rather than a finished arrival: it asks them to
finish or clear the truck instead of silently dropping what they entered.
An untouched form receives just the staged trucks.

With no trucks staged the drawer sends the original truckEntrance and
containerNumbers fields, so a single-truck arrival takes exactly the path
it did before.

Verified: backoffice type-check reports 982 errors both with and without
this change (all pre-existing, none in the touched files); 13 warehouse
API suites pass (73 tests).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 19:52:46 +00:00
hager
076884623e feat(warehouses): load trains from the warehouse, mirroring the schedule
Ready to Load listed inventory and offered an auto-load picker, but the
warehouse floor works train by train: a train stands at the yard, its
bookings board wagon by wagon, it rolls. That view existed only on the
train schedule workspace, which the warehouse staff do not run.

Ready to Load now has an Items / By train switch. By train is a mirror
of the schedule's own column, placed where the loading actually happens.

Nothing in it has its own rules. Train position, the per-yard loading and
unloading windows, and the per-booking Load / Wagons / Unload actions all
come from the train-scheduling endpoints the schedule workspace already
calls, so a Load that would be refused there is disabled here with the
same reason and the two surfaces cannot disagree. No train-scheduling
code is touched.

What the warehouse adds is what the schedule cannot see: which of the
train's bookings are physically in the shed, with their GRN and
inspection state, laid out along the flow the staff follow -- receive and
GRN, inspect, ready, open the loading window, train at yard, load per
wagon, dispatch, unload at port.

Wagons
- The wagon modal calls the same per-wagon journey endpoints, so every
  server gate (train at the yard, window started, PAID, GRN) is the
  schedule's own.
- Wagons go one at a time in order: the server flips the booking to
  IN_TRANSIT or ARRIVED on whichever call clears the last wagon, so
  sequential is required, not merely tidy. A failure stops the run, the
  wagons already sent stay done, and the toast says how many, so a retry
  only resends the rest.
- Deliberately not mirrored: cancelling wagons that will not ride, and
  the direct truck-to-train handover. Both are commercial decisions
  (fees, credits, GRN waiver) that belong to the schedule workspace.

loadable-trains takes includeDispatched. Loading follows the train after
it rolls, since a mid-corridor warehouse boards its cargo when the train
stands at its yard, and the train-centric view needs the same set the
schedule offers Load on. The default stays pre-dispatch only, so the
existing auto-load picker is unchanged.

Also fixes the backoffice build: ReceiveInventoryModal used MultiSelect
without importing it, left behind by the self-haul assignment work.
2026-09-03 04:38:46 +00:00
hagiye
031efce92b Truck assiggnment and per truc 2026-09-02 07:17:19 +03:00
Hagernesh
3721ac1ef0 feat(warehouses): gate loading queues on the train's station loading window
Warehouse loading queues and the train schedule page now share one truth:
the schedule's per-yard stationWorkLogs. A booking's items aren't loadable
until "Start loading" has been clicked for their boarding yard, mirroring
the same assertStationWorkStarted check the train schedule page's own Load
button already enforces.

- LoadableTrainRow/LoadableTrain carry originStationId + stationWorkLogs.
- TrainLoadableItem carries originYardId/originYardLabel/loadingWindowStarted,
  computed from station_work_logs in the same query.
- New YardLoadingWindows component surfaces the per-yard windows on the
  loading panel; ReceiveInventoryModal and LoadToTrainPanel wire it in.
- Invalidate loadable-trains/train-loadable-items/warehouse-inventory
  queries alongside train-scheduling ones, since they render off the same
  data.

Fixes the StationWorkLogJson typo that broke the freight-api build.
2026-09-01 03:43:09 +00:00
Hagernesh
7f5e8349da Intercity andd import export gating 2026-08-22 09:17:28 +00:00
Hagernesh
f9987e515c feat(warehouses): filters/pagination on all import+export queue tabs
Extends the stat-tile/filter/pagination pattern from the import arrival
queue to every remaining import and export tab (unloaded, eligible,
received, ready-to-load, loaded/dispatch, locate-booking), reusing
useListControls/ListControls/RuleEngineListFooter throughout. Adds a
5-tile stat row to the export tab bar and renames ImportStatCard to
WarehouseStatCard now that it's shared by both directions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 10:29:59 +00:00
Hagernesh
d4c51ec63a feat(warehouses): add import-ops stat tiles and arrival-queue filters
Adds Arrived/Unloaded/Dispatch Ready/Total Bookings stat tiles above the
import queue tabs, wires search+date filters and pagination into the
arrival queue table (reusing useListControls/ListControls/
RuleEngineListFooter already used by Inventory Inquiry), and adds a
copy-to-clipboard action on the truncated schedule ID.
2026-08-20 10:29:59 +00:00
Hagernesh
dfc7dfec5a fix(freight): filter container returns by returned-by truck type
Top Returned Containers table ignored the EDR/Customer tab because
the backend never persisted which truck type performed the return.
Added returned_by column + DTO/entity field, wired create payload to
send it, and filtered the table by the active tab.
2026-08-04 06:55:22 +00:00
Hagernesh
101bf69271 feat(warehouses): map GRN numbers to the goods owner
GRN-<DIR>-<DATE>-<REF8> carried no owner, so a note couldn't be
identified by who owns the cargo. Add an owner segment sourced from the
booking's company at every generation point (import, export, facility,
manual receive), keep REF8 for uniqueness, and label the GRN document
row Owner's Name.
2026-07-27 09:57:59 +00:00
Hagernesh
6983645347 feat(warehouses): gate double-handling fee on a per-booking Yes/No
Double handling billed every import with a matching rule. Add
bookings.double_handling (+ set_at/by), charge only when Yes, expose a
PATCH endpoint (import-only, locked after invoicing, audited) and Yes/No
items in the inventory row menu.
2026-07-25 10:45:34 +00:00
Hagernesh
1fae0752a0 Ticket: Warehouse dashboard Needs Attention cards — fix trucks-on-site aging counters (EDR trucks, UNLOADED status) and make each card open the exact list behind its count via new inventory drill-down filters. 2026-07-21 11:03:40 +00:00
Hagernesh
da44752c70 SELECT BOX 2026-07-11 08:30:39 +00:00
Hagernesh
707923b603 feat(warehouses): expandable booking rows list containers / cargo inline
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>
2026-07-10 11:13:21 +00:00
Hagernesh
3ff4b8bf94 fix(warehouses): finish queue-table ergonomics batch
- 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>
2026-07-10 11:13:21 +00:00
Hagernesh
aa3674041d fix(warehouses): drop truncated UUID columns and duplicate GRN buttons
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>
2026-07-10 11:13:21 +00:00
Hagernesh
9eb10bb4ce feat(warehouses): auto-load targets a selected train, never loads trainless
"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>
2026-07-10 08:34:14 +00:00
Hagernesh
a7b429544b fix(warehouses): trim receive form to fields with a real source
- 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>
2026-07-10 07:11:20 +00:00
Hagernesh
fc2366f9e0 feat(warehouses): prefill receive-to-warehouse from existing booking data
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>
2026-07-10 07:11:20 +00:00
Hagernesh
58a66b3a23 fix(warehouses): retire per-booking Load and Dispatch buttons
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>
2026-07-09 15:37:07 +00:00
Hagernesh
7ab5e9edd5 feat(warehouses): handover-driven approve delivery, weigh-skip, 5-min sign reminders
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>
2026-07-09 15:37:06 +00:00
Hagernesh
7bf407b503 fix(warehouses): surface the real reason when a PDF download fails
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>
2026-07-09 15:37:06 +00:00
Hagernesh
80904eeeb2 Handover SIGN notification, resending until signed no exit before sign 2026-07-08 09:33:19 +00:00
Hagernesh
3f6b9ac974 Container truck assignment on customer portal for import 2026-07-08 04:36:03 +00:00
Hagernesh
96b78fc72e Login fixed — freight-api boots (PgBouncer search_path + migrations-table reconcile).
Invoice PDF styled (vector fallback, no Docker change).
Marshalling doc styled (table-aware fallback, portrait/landscape).
Load to Train tab — per-train arrived containers → multiselect → load onto wagons.
Yard/zone list filtered by freight type.
2026-07-06 21:09:56 +00:00
Hagernesh
692d9074d0 feat: container/bulk items datatable on warehouse view details
Adds a per-container/bulk items view for a booking with derived lifecycle
stage (PENDING -> RECEIVED -> GRN -> LOADED -> LEFT -> DELIVERED) and reference
badges (booking, contract, last-mile). Backend containerItems() aggregates
booking_container_units + customer_truck_containers + inventory; exposed at
GET warehouse-inventory/bookings/:id/container-items.

Frontend ContainerItemsModal (opened from the View Details eye): stage tabs
with counts, ref columns, checkbox multiselect of loadable items -> pick truck
-> load (Truck_dispatch), and per-row per-truck Exit Paper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 13:46:16 +00:00
Hagernesh
551a5fdeed list trucks + loadable containers → load) 2026-07-06 12:40:34 +00:00
Hagernesh
fe9503638b separte handover edr lastmile and customer last mile flow 2026-07-06 12:27:58 +00:00
hagiye
ad9f9de7a8 Truck Assign by customer plus Handover signature on portal 2026-07-02 12:32:18 +03:00
hagiye
129448e437 frist mile receive to warehouse and last mile truck arrival integration 2026-07-02 01:08:37 +03:00
hagiye
14a00af341 train gate pass, Telebirr and Wafi 2026-07-01 16:23:52 +03:00
hagiye
9f5c22c1ee Goods recieved notes and Booking delivery 2026-06-30 07:19:12 +03:00
hagiye
d55f9312bf Marshaling document Receive Export and import handover to customer 2026-06-29 12:24:14 +03:00
hagiye
fee4365b70 Import operation gate pass 2026-06-27 21:04:27 +03:00
hagiye
11803763b0 Invoice and clearance seal approval 2026-06-27 05:11:44 +03:00
hagiye
108d91b5fa Invoice and clearance seal approval 2026-06-27 04:58:21 +03:00
hagiye
fafa64e4db Warehouse receiving for export and GRN generation 2026-06-27 04:20:23 +03:00
hagiye
15be978c42 Warehouse receiving for export and GRN generation 2026-06-27 04:19:32 +03:00
hagiye
8005282f9e Invoice and clearance seal approval 2026-06-27 00:39:48 +03:00
hagiye
1d84cad99d Invoice genaration and seal 2026-06-27 00:22:38 +03:00
hagiye
e6c2735f9b Fix inventory status column 2026-06-25 13:06:11 +03:00
Nathnael
d66ccc5363 refactor: migrated of the customer hooks for warehouse data fetching 2026-06-22 12:25:20 +00:00
Hagernesh
41d21edae5 feat(warehouse): Batch 10 — import post-inspection actions (pickup / last-mile / store / dispatch)
- READY_FOR_PICKUP transitions now → DELIVERED (customer pickup) OR DISPATCHED (dispatch out) OR
  STORED (operator-chosen storage) — pickup and dispatch kept separate. No auto-storage.
- GET /warehouse-inventory/import/pickup-ready-queue: READY_FOR_PICKUP import items (shared query
  with the unloaded queue, route-derived import filter)
- WarehouseInventoryTable: PICKUP_READY rows now show explicit Store + Dispatch buttons alongside
  the customer-pickup (release/deliver) next action; reuses the existing advance() dispatcher
- Import → Dispatch Queue tab renders pickup-ready items via InventoryWorkbench (all existing
  actions + modals intact) with Last Mile shown only when door delivery was requested
- Verified: pickup-ready item → Store → STORED; → Dispatch → DISPATCHED

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 14:38:23 +00:00
Hagernesh
040ba6d495 feat(warehouse): Batch 9 — Import Unloaded Queue + bulk destination inspection
- bulkMarkInspected: UNLOADED added to eligible statuses; IMPORT passed items now advance to
  READY_FOR_PICKUP (pickup ready) instead of READY_FOR_LOADING (export unchanged)
- GET /warehouse-inventory/import/unloaded-queue: UNLOADED import items with the inspection-screen
  columns (booking, customer, arrival, container, cargo type, weight, train schedule, inspection
  status, pickup option, last-mile, current status); route-derived import filter
- Dedicated ImportUnloadedQueueTab replaces the Batch 8 workbench reuse: full spec columns,
  Select All / Unselect All / selected count / Mark Selected as Inspected, plus per-row
  Inspect / Report (InspectionReportModal) for damage / image / weight-loss detail
- Verified: import item UNLOADED → bulk inspect → READY_FOR_PICKUP (PASSED), drops out of queue

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 14:15:22 +00:00
Hagernesh
8b28a7c430 feat(warehouse): Batch 8 — Import Auto Unload Arrived Bookings (→ UNLOADED)
- New UNLOADED inventory status (train-arrival landing state) + transitions + unloaded_at column
  (idempotent migration 1791000000003) + INVENTORY_UNLOADED activity type
- POST /warehouse-inventory/import/auto-unload-arrived-bookings { scheduleId }: validates ARRIVED
  IMPORT train, unloads all eligible assigned bookings (IN_TRANSIT/ARRIVED_AT_*) into UNLOADED,
  records unloadedAt + activity. Does NOT store and does NOT inspect. Reuses allocation + inventory
  plumbing. Returns { unloadedCount, skippedCount, failedCount, results }.
- Arrive Queue "Auto Unload Arrived Bookings" button now calls the new endpoint (was per-booking loop)
- Import → Unloaded Queue tab: lists UNLOADED items via InventoryWorkbench (existing actions preserved:
  Inspect/Store/Move/History) + a Last Mile action shown ONLY when booking requested door delivery
- Batch8TestDataSeeder: sets seed import train bookings to IN_TRANSIT (unload-eligible)
- Verified: auto-unload → 1 UNLOADED (unloadedAt set, not stored, not inspected); ineligible skipped;
  idempotent re-run skips already-unloaded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 12:57:29 +00:00
Hagernesh
35c3341baf feat(warehouse): Batch 7 — Import Arrive Queue (arrived import trains, read-only)
- SchedulingReadFacade: importArriveQueue() + importTrainDetail() — read-only SELECTs only,
  direction derived from origin/destination station countries (route-based), so EXPORT/DOMESTIC
  trains are excluded. Per-train booking/container/cargo counts.
- GET /warehouse-inventory/import/arrive-queue + /import/trains/:scheduleId/items
- Import tab restructured into sub-tabs: Arrive Queue (implemented) / Unloaded Queue /
  Dispatch Queue (placeholders for next batch)
- ImportArriveQueueTab: train table (schedule id, train #, route, origin, destination, arrival,
  bookings/containers/cargoes, status) with Open (expandable assigned-items detail) + Auto Unload
  (reuses existing per-booking unload endpoint over the train's bookings)
- Batch7TestDataSeeder: 1 arrived IMPORT train (SEED-IMP-001) + 1 arrived EXPORT train
  (SEED-EXP-001, proves exclusion); idempotent
- Train schedule service logic untouched (facade is SELECT-only)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 11:15:31 +00:00
Hagernesh
a8bc5b19de feat(warehouse): fetch all PAID bookings in one call, classified IMPORT/EXPORT by route
- eligibleBookings(direction?) now returns all import+export bookings when direction omitted
  (DOMESTIC excluded); per-direction filter preserved for backward compatibility
- GET /warehouse-inventory/eligible-bookings (no query) returns the combined set
- Frontend: single shared useEligibleBookings() query; both Receive tabs filter client-side
  by route-derived direction, so only one HTTP request fires

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 20:15:26 +00:00
Hagernesh
0ab508e377 feat(warehouse): Batch 6 — Export Loaded queue + Dispatch Queue with bulk dispatch
- GET /warehouse-inventory/loaded-export: EXPORT+LOADED items (route-derived direction)
- POST /warehouse-inventory/bulk-dispatch-export: reuses existing dispatch() transition
  (LOADED → DISPATCHED, capacity freed, movement/activity logged); skips non-LOADED/non-EXPORT
- Extracted shared exportInventoryByStatus() helper (readyToLoadExport now delegates to it)
- Frontend LoadedExportTab serves both Loaded (read-only) and Dispatch Queue (dispatchable)
  sub-tabs with Dispatch / Dispatch Selected / Dispatch All + per-row Dispatch
- Replaces "Loaded" and "Dispatch Queue — coming in the next batch" placeholders
- Train/schedule flow (DISPATCHED → IN_TRANSIT) unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 20:04:18 +00:00
Hagernesh
806fc4a8ca feat(warehouse): Batch 5 — Export Ready To Load queue + Auto Load Ready Items
- GET /warehouse-inventory/ready-to-load-export: EXPORT+PASSED+READY_FOR_LOADING items
  with booking details (customer, container, cargo type, route, inspection status)
- Direction filtering via deriveTradeDirection (route-based, not stored field)
- Frontend ReadyToLoadTab: full table (checkbox, booking ref/id, customer, container,
  cargo type, weight, route, inspection status, current status) with selection
- Auto Load Ready Items button reuses existing POST /load-passed-export endpoint
- Replaces "Ready To Load — coming in the next batch" placeholder in Export sub-tabs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 18:03:11 +00:00
Hagernesh
50c6381341 feat(warehouse): Batch 3+4 — export receive-selected label + bulk-mark-inspected workflow
- BulkReceiveModal: Receive Selected shows "received at facility" for EXPORT
- Export inspection: POST /warehouse-inventory/bulk-mark-inspected reuses WarehouseInspectionService.create
- EXPORT items advance to READY_FOR_LOADING after inspection PASSED
- InventoryWorkbench: selection state + "Mark Selected as Inspected" bulk button
- WarehouseInventoryTable: optional Checkbox column for bulk selection
- Route-based direction in eligibleBookings, bulkReceive, getBookingDirection
- New BulkInspectDto; service + controller wired

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 17:52:01 +00:00
Hagernesh
b7a831b063 feat(warehouse): route-based receive direction + Export Receive Queue (Batch 2)
- Direction derived from route via existing deriveTradeDirection (eligible-bookings,
  bulk receive guard, getBookingDirection) instead of stored trade_direction
- Export tab sub-tabs (Receive Queue + Ready-to-Load/Loaded/Dispatch placeholders)
- Receive Queue: full column set + per-row Receive; eligible-bookings adds customerId
- create/update warehouse: map DB errors to 400; dashboard: distinct per-status colors

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:26:50 +00:00