Files
edr-platform/apps/edr-freight-api/src/modules/contracts/dto/create-booking-under-contract.dto.ts

181 lines
4.5 KiB
TypeScript

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsDateString,
IsIn,
IsInt,
IsNumber,
IsOptional,
IsString,
IsUUID,
Matches,
Min,
ValidateNested,
} from 'class-validator';
/** Per-shipment equipment return — "NA" stays contract-level only. */
const SHIPMENT_EQUIPMENT_RETURNS = ['WITH_RETURN', 'WITHOUT_RETURN'] as const;
/** One physical container under a booking line — entered at booking time. */
export class CreateContainerUnitDto {
@ApiProperty({ description: 'ISO 6346 container number, e.g. ABCD1234567' })
@IsString()
@Transform(({ value }) => (typeof value === 'string' ? value.trim().toUpperCase() : value))
@Matches(/^[A-Z]{4}\d{7}$/, {
message: 'containerNumber must match ISO container format, e.g. ABCD1234567',
})
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;
@ApiPropertyOptional({
minimum: 0,
description:
'How many units of this line ship with empty-container return (≤ quantity). ' +
'Only allowed when the contract was created WITH_RETURN (container freight).',
})
@IsOptional()
@IsInt()
@Min(0)
@Transform(({ value }) => Number(value))
returnQuantity?: 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({
enum: SHIPMENT_EQUIPMENT_RETURNS,
description:
'Per-shipment equipment return override; omitted → the contract default applies.',
})
@IsOptional()
@IsIn([...SHIPMENT_EQUIPMENT_RETURNS])
equipmentReturn?: 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;
}