import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsNotEmpty, IsString, IsNumber, Min, IsOptional, ValidateNested, IsBoolean, } from 'class-validator'; import { Type } from 'class-transformer'; import { RateSearchFiltersDto } from './rate-search-filters.dto'; export class CsvRateSearchDto { @ApiProperty({ description: 'Origin UN/LOCODE', example: 'FRFOS' }) @IsNotEmpty() @IsString() origin: string; @ApiProperty({ description: 'Destination UN/LOCODE', example: 'CNSHA' }) @IsNotEmpty() @IsString() destination: string; @ApiProperty({ description: 'Volume in cubic meters (CBM)', minimum: 0.01, example: 10.5 }) @IsNotEmpty() @IsNumber() @Min(0.01) volumeCBM: number; @ApiProperty({ description: 'Weight in kilograms', minimum: 1, example: 2500 }) @IsNotEmpty() @IsNumber() @Min(1) weightKG: number; @ApiPropertyOptional({ description: 'Container type filter', example: 'LCL' }) @IsOptional() @IsString() containerType?: string; @ApiPropertyOptional({ description: 'Cargo contains dangerous goods', example: false }) @IsOptional() @IsBoolean() hasDangerousGoods?: boolean; @ApiPropertyOptional({ description: 'Advanced filters', type: RateSearchFiltersDto }) @IsOptional() @ValidateNested() @Type(() => RateSearchFiltersDto) filters?: RateSearchFiltersDto; } export class CsvRateSearchResponseDto { @ApiProperty({ description: 'Array of matching rate results', type: [Object] }) results: CsvRateResultDto[]; @ApiProperty({ description: 'Total number of results', example: 12 }) totalResults: number; @ApiProperty({ description: 'CSV files searched', type: [String] }) searchedFiles: string[]; @ApiProperty({ description: 'Timestamp of search', example: '2026-05-11T10:30:00Z' }) searchedAt: Date; @ApiProperty({ description: 'Applied filters' }) appliedFilters: RateSearchFiltersDto; } export class FobBreakdownDto { documentation: number; isps: number; handling: number; solas: number; customs: number; ams_aci: number; isf5: number; dgAdmin: number; } export class PriceBreakdownDto { @ApiProperty({ description: 'Freight charge', example: 420.0 }) freightCharge: number; @ApiProperty({ description: 'Freight currency', example: 'USD' }) freightCurrency: string; @ApiProperty({ description: 'Fixed FOB charges (doc+ISPS+solas+customs+AMS+ISF5)', example: 185 }) fobFixed: number; @ApiProperty({ description: 'FOB handling charge', example: 60 }) fobHandling: number; @ApiProperty({ description: 'DG admin fee (FOB currency, 0 if non-DG)', example: 0 }) fobDG: number; @ApiProperty({ description: 'FOB currency', example: 'EUR' }) fobCurrency: string; @ApiProperty({ description: 'Itemized FOB breakdown', type: FobBreakdownDto }) fobBreakdown: FobBreakdownDto; @ApiPropertyOptional({ description: 'DG surcharge amount (null if on_request/not_accepted)', example: null, }) dgSurchargeAmount: number | null; @ApiProperty({ description: 'DG surcharge currency', example: 'EUR' }) dgSurchargeCurrency: string; @ApiProperty({ description: 'DG surcharge status', enum: ['computed', 'on_request', 'not_accepted'], example: 'computed', }) dgSurchargeStatus: string; @ApiProperty({ description: 'Total freight in freightCurrency', example: 420.0 }) totalFreight: number; @ApiProperty({ description: 'Total FOB in fobCurrency', example: 245 }) totalFob: number; @ApiProperty({ description: 'Sum for sorting (currency-naive)', example: 665.0 }) totalPriceForSorting: number; @ApiProperty({ description: 'Primary currency', example: 'USD' }) primaryCurrency: string; } export class CsvRateResultDto { @ApiProperty({ example: 'SSC Consolidation' }) companyName: string; @ApiProperty({ example: 'bookings@ssc.com' }) companyEmail: string; @ApiProperty({ description: 'Origin CFS name', example: 'Fos Sur Mer' }) originCFS: string; @ApiProperty({ description: 'Origin UN/LOCODE', example: 'FRFOS' }) origin: string; @ApiProperty({ description: 'Port of loading', example: 'FOS SUR MER' }) portOfLoading: string; @ApiProperty({ description: 'Routing type', example: 'Direct' }) routing: string; @ApiProperty({ description: 'Destination CFS name', example: 'Shanghai' }) destinationCFS: string; @ApiProperty({ description: 'Destination UN/LOCODE', example: 'CNSHA' }) destination: string; @ApiProperty({ description: 'Destination country', example: 'China' }) destinationCountry: string; @ApiProperty({ example: 'LCL' }) containerType: string; @ApiProperty({ description: 'Detailed price breakdown', type: PriceBreakdownDto }) priceBreakdown: PriceBreakdownDto; @ApiProperty({ description: 'Departure frequency', example: 'Weekly' }) frequency: string; @ApiProperty({ description: 'Transit time (adjusted if service level)', example: 28 }) transitDays: number; @ApiProperty({ description: 'Rate validity end date', example: '2026-12-31' }) validUntil: string; @ApiProperty({ description: 'Whether DG cargo is accepted', example: true }) dgAccepted: boolean; @ApiProperty({ description: 'DG surcharge status', example: 'computed' }) dgSurchargeStatus: string; @ApiProperty({ description: 'Internal remarks', example: 'GR1/GR2' }) remarks: string; @ApiProperty({ example: 'CSV' }) source: 'CSV'; @ApiProperty({ description: 'Match score 0-100', example: 95 }) matchScore: number; @ApiPropertyOptional({ enum: ['RAPID', 'STANDARD', 'ECONOMIC'] }) serviceLevel?: string; @ApiPropertyOptional({ description: 'Price multiplier for service level', example: 1.0 }) priceMultiplier?: number; @ApiPropertyOptional({ description: 'Original transit days before service level adjustment', example: 28, }) originalTransitDays?: number; }