feat(bookings): surface cargo declared on the shipment request

A GENERAL + customs contract does not let the customer book directly: they
submit a shipment request, and initiateForShipmentRequest opens a BARE booking
from it — "the request itself carries the quantities; the instance carries
none". Between initiation and completeUnderContract the booking legitimately
holds no cargo, so the export reported 0 containers for a customer who had
declared, say, 2 x 20FT. 23 bookings on dev data are in that state.

Adds two columns and one filter reading booking_requests.requested_lines:
- "Requested cargo" — the declared lines as text ("2 x 20FT"), handling the
  bulk shape too (tons / item count), not only containers.
- "Requested containers" — the declared box count, with a matching min/max
  filter on the list and the export.

Deliberately a separate column rather than a fallback inside the real container
count: a declared 2 x 20FT is a request, not two boxes on a booking, and
merging them would overstate operational totals. The two compose instead —
Containers = 0 AND Requested containers >= 1 is exactly the set awaiting
completion after clearance.

requested_lines is free-form jsonb, so the container array is guarded by
jsonb_typeof before jsonb_array_elements; one malformed row would otherwise
500 the whole list.
This commit is contained in:
Nathnael
2026-08-28 11:23:52 +00:00
parent aa3100d161
commit df8e10e041
8 changed files with 162 additions and 0 deletions

View File

@@ -251,6 +251,25 @@ export default function BookingRequestsPage() {
? { containersMin: v.v[0], containersMax: v.v[1] }
: { containersMin: v.v[0], containersMax: v.v[0] },
},
{
// Declared on the shipment request, not yet on the booking. Pair it
// with Containers = 0 to find the set awaiting completion.
key: "requestedContainers",
label: "Requested containers",
type: "number",
secondary: true,
operators: ["is", "between"],
toParams: (v) =>
v.op === "between"
? {
requestedContainersMin: v.v[0],
requestedContainersMax: v.v[1],
}
: {
requestedContainersMin: v.v[0],
requestedContainersMax: v.v[0],
},
},
{
key: "serviceTypeId",
label: "Service",