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

@@ -103,6 +103,19 @@ export class FilterBookingDto {
@Transform(({ value }) => (value === '' || value == null ? undefined : Number(value)))
containersMax?: number;
@ApiPropertyOptional({
description:
'Minimum containers declared on the shipment request behind the booking',
})
@IsOptional()
@Transform(({ value }) => (value === '' || value == null ? undefined : Number(value)))
requestedContainersMin?: number;
@ApiPropertyOptional({ description: 'Maximum requested containers — see requestedContainersMin' })
@IsOptional()
@Transform(({ value }) => (value === '' || value == null ? undefined : Number(value)))
requestedContainersMax?: number;
@ApiPropertyOptional({ enum: FREIGHT_TYPES })
@IsOptional()
@IsIn([...FREIGHT_TYPES])