import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { IsArray, IsBoolean, IsDateString, IsInt, IsNumber, IsOptional, IsString, IsUUID, Min, ValidateNested, } from 'class-validator'; /** One physical container under a booking line — entered at booking time. */ export class CreateContainerUnitDto { @ApiProperty() @IsString() containerNumber!: string; @ApiPropertyOptional() @IsOptional() @IsString() sealNumber?: string; @ApiProperty({ description: 'VGM in tons', minimum: 0 }) @IsNumber() @Min(0) @Transform(({ value }) => Number(value)) vgmTons!: number; @ApiPropertyOptional({ default: false }) @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true' || value === true) isHazardous?: boolean; @ApiPropertyOptional({ default: false }) @IsOptional() @IsBoolean() @Transform(({ value }) => value === 'true' || value === true) isReefer?: boolean; } export class CreateBookingContainerLineDto { @ApiProperty({ description: '"20ft" | "40ft" — must be in the contract scope' }) @IsString() containerSize!: string; @ApiProperty({ minimum: 1 }) @IsInt() @Min(1) @Transform(({ value }) => Number(value)) quantity!: number; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsInt() @Min(0) @Transform(({ value }) => Number(value)) hazardousQuantity?: number; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsInt() @Min(0) @Transform(({ value }) => Number(value)) reeferQuantity?: number; @ApiProperty({ type: [CreateContainerUnitDto] }) @IsArray() @ValidateNested({ each: true }) @Type(() => CreateContainerUnitDto) units!: CreateContainerUnitDto[]; } export class CreateBulkLineDto { @ApiPropertyOptional({ format: 'uuid' }) @IsOptional() @IsUUID() cargoTypeId?: string | null; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsNumber() @Min(0) @Transform(({ value }) => Number(value)) cargoWeightTons?: number; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsInt() @Min(0) @Transform(({ value }) => Number(value)) itemCount?: number; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsInt() @Min(0) @Transform(({ value }) => Number(value)) hazardousQuantity?: number; @ApiPropertyOptional({ minimum: 0 }) @IsOptional() @IsInt() @Min(0) @Transform(({ value }) => Number(value)) reeferQuantity?: number; } /** Shipment booking created under a contract (Path A customer, Path B GL ET). */ export class CreateBookingUnderContractDto { @ApiPropertyOptional({ format: 'uuid', description: 'Required for GENERAL multi-route contracts; ONE_TIME auto-selected.', }) @IsOptional() @IsUUID() contractRouteId?: string; @ApiPropertyOptional({ description: 'Binding shipment day. Omitted for intercity (DOMESTIC) bookings — staff assign a passing train later.', example: '2026-07-15', }) @IsOptional() @IsDateString() scheduledDate?: string; @ApiPropertyOptional({ type: [CreateBookingContainerLineDto] }) @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => CreateBookingContainerLineDto) containers?: CreateBookingContainerLineDto[]; @ApiPropertyOptional({ type: [CreateBulkLineDto] }) @IsOptional() @IsArray() @ValidateNested({ each: true }) @Type(() => CreateBulkLineDto) bulkLines?: CreateBulkLineDto[]; @ApiPropertyOptional() @IsOptional() @IsString() notes?: string; }