import { BadRequestException } from '@nestjs/common'; import { CARGO_CONDITIONS, CargoCondition, FREIGHT_TYPES, FreightType } from './entities/booking.entity'; import { BookingFreightShapeInput } from './dto/validators/booking-freight.validator'; /** Normalize and validate booking freight shape (used on create and after update merge). */ export function assertFreightShape(input: BookingFreightShapeInput): void { if (!input.freightType || !FREIGHT_TYPES.includes(input.freightType as FreightType)) { throw new BadRequestException( `freightType is required and must be one of: ${FREIGHT_TYPES.join(', ')}`, ); } // const condition = input.cargoCondition ?? 'LADEN'; if (!CARGO_CONDITIONS.includes(condition as CargoCondition)) { throw new BadRequestException( `cargoCondition must be one of: ${CARGO_CONDITIONS.join(', ')}`, ); } const containers = input.containers ?? []; const hasContainers = containers.length > 0; const hasCargoType = Boolean(input.cargoTypeId); // Empty means bare equipment: there is no commodity to name, and bulk has no // equipment of its own to move, so EMPTY only ever rides CONTAINER freight. if (condition === 'EMPTY' && input.freightType !== 'CONTAINER') { throw new BadRequestException( 'An empty booking must be CONTAINER freight — bulk carries no equipment', ); } if (input.freightType === 'BULK') { if (hasContainers) { throw new BadRequestException( 'BULK freight cannot include container lines; use cargoTypeId only', ); } if (!hasCargoType) { throw new BadRequestException('cargoTypeId is required for BULK freight'); } return; } if (hasCargoType) { throw new BadRequestException('cargoTypeId is not allowed for CONTAINER freight'); } if (!hasContainers) { throw new BadRequestException( 'CONTAINER freight requires at least one container line with containerTypeId', ); } for (const line of containers) { if (!line.containerTypeId) { throw new BadRequestException('Each container line must include containerTypeId'); } } }