mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-06 08:53:39 +00:00
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:
@@ -8,6 +8,8 @@ import {
|
||||
bookingContainerVgmSql,
|
||||
bookingContentSql,
|
||||
bookingHasContainerTypeSql,
|
||||
bookingRequestedCargoSql,
|
||||
bookingRequestedContainerCountSql,
|
||||
} from '../../bookings/booking-content.sql';
|
||||
import { bookingTonsSql } from '../../bookings/booking-tons.sql';
|
||||
import { Booking } from '../../bookings/entities/booking.entity';
|
||||
@@ -36,6 +38,7 @@ const REVENUE = 'COALESCE(b.adjusted_total_amount, b.total_amount)';
|
||||
/** What the customer described as the booking's contents — see the helper. */
|
||||
const CONTENT = bookingContentSql('b');
|
||||
const CONTAINER_COUNT = bookingContainerCountSql('b');
|
||||
const REQUESTED_COUNT = bookingRequestedContainerCountSql('b');
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
'DRAFT', 'SUBMITTED', 'UNDER_REVIEW', 'APPROVED', 'REJECTED',
|
||||
@@ -210,6 +213,9 @@ export const bookingsDataset: ExportDataset = {
|
||||
{ key: 'cargoDescription', label: 'Cargo description', type: 'string', group: 'cargo', select: 'b.cargo_free_text' },
|
||||
// Boxes, not lines: booking_container is one row per LINE with a quantity.
|
||||
{ key: 'containerCount', label: 'Containers', type: 'number', group: 'cargo', default: true, select: `${CONTAINER_COUNT}::int`, sortExpr: CONTAINER_COUNT },
|
||||
// Declared on the shipment request, not yet on the booking — see the helper.
|
||||
{ key: 'requestedCargo', label: 'Requested cargo', type: 'string', group: 'cargo', select: bookingRequestedCargoSql('b') },
|
||||
{ key: 'requestedContainers', label: 'Requested containers', type: 'number', group: 'cargo', select: `${REQUESTED_COUNT}::int`, sortExpr: REQUESTED_COUNT },
|
||||
{ key: 'freightType', label: 'Freight type', type: 'string', group: 'cargo', default: true, select: 'b.freight_type' },
|
||||
{ key: 'tons', label: 'Tonnage', type: 'tons', group: 'cargo', default: true, select: `ROUND(${TONS})::float8`, sortExpr: TONS },
|
||||
// The per-line sum, NOT b.cargo_total_weight_vgm — the portal leaves that
|
||||
@@ -296,6 +302,8 @@ export const bookingsDataset: ExportDataset = {
|
||||
{ key: 'containerTypeId', label: 'Container type', type: 'select', optionsQuery: containerTypeOptions },
|
||||
{ key: 'containersMin', label: 'Containers (min)', type: 'text' },
|
||||
{ key: 'containersMax', label: 'Containers (max)', type: 'text' },
|
||||
{ key: 'requestedContainersMin', label: 'Requested containers (min)', type: 'text' },
|
||||
{ key: 'requestedContainersMax', label: 'Requested containers (max)', type: 'text' },
|
||||
{ key: 'companyId', label: 'Customer', type: 'text' },
|
||||
{ key: 'search', label: 'Search reference or customer', type: 'text' },
|
||||
],
|
||||
@@ -329,6 +337,10 @@ export const bookingsDataset: ExportDataset = {
|
||||
const max = num(params.containersMax);
|
||||
if (Number.isFinite(min)) qb.andWhere(`${containerCount} >= :containersMin`, { containersMin: min });
|
||||
if (Number.isFinite(max)) qb.andWhere(`${containerCount} <= :containersMax`, { containersMax: max });
|
||||
const reqMin = num(params.requestedContainersMin);
|
||||
const reqMax = num(params.requestedContainersMax);
|
||||
if (Number.isFinite(reqMin)) qb.andWhere(`${REQUESTED_COUNT} >= :requestedContainersMin`, { requestedContainersMin: reqMin });
|
||||
if (Number.isFinite(reqMax)) qb.andWhere(`${REQUESTED_COUNT} <= :requestedContainersMax`, { requestedContainersMax: reqMax });
|
||||
if (params.paymentStatus) qb.andWhere('b.payment_status = :paymentStatus', { paymentStatus: params.paymentStatus });
|
||||
if (params.companyId) qb.andWhere('b.company_id = :companyId', { companyId: params.companyId });
|
||||
if (params.search) {
|
||||
|
||||
Reference in New Issue
Block a user