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'; /** * CSV Rate Search Request DTO * * Request body for searching rates in CSV-based system * Includes basic search parameters + optional advanced filters */ export class CsvRateSearchDto { @ApiProperty({ description: 'Origin port code (UN/LOCODE format)', example: 'NLRTM', pattern: '^[A-Z]{2}[A-Z0-9]{3}$', }) @IsNotEmpty() @IsString() origin: string; @ApiProperty({ description: 'Destination port code (UN/LOCODE format)', example: 'USNYC', pattern: '^[A-Z]{2}[A-Z0-9]{3}$', }) @IsNotEmpty() @IsString() destination: string; @ApiProperty({ description: 'Volume in cubic meters (CBM)', minimum: 0.01, example: 25.5, }) @IsNotEmpty() @IsNumber() @Min(0.01) volumeCBM: number; @ApiProperty({ description: 'Weight in kilograms', minimum: 1, example: 3500, }) @IsNotEmpty() @IsNumber() @Min(1) weightKG: number; @ApiPropertyOptional({ description: 'Number of pallets (0 if no pallets)', minimum: 0, example: 10, default: 0, }) @IsOptional() @IsNumber() @Min(0) palletCount?: number; @ApiPropertyOptional({ description: 'Container type filter (e.g., LCL, 20DRY, 40HC)', example: 'LCL', }) @IsOptional() @IsString() containerType?: string; @ApiPropertyOptional({ description: 'Advanced filters for narrowing results', type: RateSearchFiltersDto, }) @IsOptional() @ValidateNested() @Type(() => RateSearchFiltersDto) filters?: RateSearchFiltersDto; // Service requirements for detailed price calculation @ApiPropertyOptional({ description: 'Cargo contains dangerous goods (DG)', example: true, default: false, }) @IsOptional() @IsBoolean() hasDangerousGoods?: boolean; @ApiPropertyOptional({ description: 'Requires special handling', example: true, default: false, }) @IsOptional() @IsBoolean() requiresSpecialHandling?: boolean; @ApiPropertyOptional({ description: 'Requires tailgate lift', example: false, default: false, }) @IsOptional() @IsBoolean() requiresTailgate?: boolean; @ApiPropertyOptional({ description: 'Requires securing straps', example: true, default: false, }) @IsOptional() @IsBoolean() requiresStraps?: boolean; @ApiPropertyOptional({ description: 'Requires thermal protection cover', example: false, default: false, }) @IsOptional() @IsBoolean() requiresThermalCover?: boolean; @ApiPropertyOptional({ description: 'Contains regulated products requiring special documentation', example: false, default: false, }) @IsOptional() @IsBoolean() hasRegulatedProducts?: boolean; @ApiPropertyOptional({ description: 'Requires delivery appointment', example: true, default: false, }) @IsOptional() @IsBoolean() requiresAppointment?: boolean; } /** * CSV Rate Search Response DTO * * Response containing matching rates with calculated prices */ export class CsvRateSearchResponseDto { @ApiProperty({ description: 'Array of matching rate results', type: [Object], // Will be replaced with RateResultDto }) results: CsvRateResultDto[]; @ApiProperty({ description: 'Total number of results found', example: 15, }) totalResults: number; @ApiProperty({ description: 'CSV files that were searched', type: [String], example: ['ssc-consolidation.csv', 'ecu-worldwide.csv'], }) searchedFiles: string[]; @ApiProperty({ description: 'Timestamp when search was executed', example: '2025-10-23T10:30:00Z', }) searchedAt: Date; @ApiProperty({ description: 'Filters that were applied to the search', type: RateSearchFiltersDto, }) appliedFilters: RateSearchFiltersDto; } /** * Surcharge Item DTO */ export class SurchargeItemDto { @ApiProperty({ description: 'Surcharge code', example: 'DG_FEE', }) code: string; @ApiProperty({ description: 'Surcharge description', example: 'Dangerous goods fee', }) description: string; @ApiProperty({ description: 'Surcharge amount in currency', example: 65.0, }) amount: number; @ApiProperty({ description: 'Type of surcharge calculation', enum: ['FIXED', 'PER_UNIT', 'PERCENTAGE'], example: 'FIXED', }) type: 'FIXED' | 'PER_UNIT' | 'PERCENTAGE'; } /** * Price Breakdown DTO */ export class PriceBreakdownDto { @ApiProperty({ description: 'Base price before any charges', example: 0, }) basePrice: number; @ApiProperty({ description: 'Charge based on volume (CBM)', example: 150.0, }) volumeCharge: number; @ApiProperty({ description: 'Charge based on weight (KG)', example: 25.0, }) weightCharge: number; @ApiProperty({ description: 'Charge for pallets', example: 125.0, }) palletCharge: number; @ApiProperty({ description: 'List of all surcharges', type: [SurchargeItemDto], }) surcharges: SurchargeItemDto[]; @ApiProperty({ description: 'Total of all surcharges', example: 242.0, }) totalSurcharges: number; @ApiProperty({ description: 'Total price including all charges', example: 542.0, }) totalPrice: number; @ApiProperty({ description: 'Currency of the pricing', enum: ['USD', 'EUR'], example: 'USD', }) currency: string; } /** * Single CSV Rate Result DTO */ export class CsvRateResultDto { @ApiProperty({ description: 'Company name', example: 'SSC Consolidation', }) companyName: string; @ApiProperty({ description: 'Company email for booking requests', example: 'bookings@sscconsolidation.com', }) companyEmail: string; @ApiProperty({ description: 'Origin port code', example: 'NLRTM', }) origin: string; @ApiProperty({ description: 'Destination port code', example: 'USNYC', }) destination: string; @ApiProperty({ description: 'Container type', example: 'LCL', }) containerType: string; @ApiProperty({ description: 'Calculated price in USD', example: 1850.5, }) priceUSD: number; @ApiProperty({ description: 'Calculated price in EUR', example: 1665.45, }) priceEUR: number; @ApiProperty({ description: 'Primary currency of the rate', enum: ['USD', 'EUR'], example: 'USD', }) primaryCurrency: string; @ApiProperty({ description: 'Detailed price breakdown with all charges', type: PriceBreakdownDto, }) priceBreakdown: PriceBreakdownDto; @ApiProperty({ description: 'Whether this rate has separate surcharges', example: true, }) hasSurcharges: boolean; @ApiProperty({ description: 'Details of surcharges if any', example: 'BAF+CAF included', nullable: true, }) surchargeDetails: string | null; @ApiProperty({ description: 'Transit time in days', example: 28, }) transitDays: number; @ApiProperty({ description: 'Rate validity end date', example: '2025-12-31', }) validUntil: string; @ApiProperty({ description: 'Source of the rate', enum: ['CSV', 'API'], example: 'CSV', }) source: 'CSV' | 'API'; @ApiProperty({ description: 'Match score (0-100) indicating how well this rate matches the search', minimum: 0, maximum: 100, example: 95, }) matchScore: number; }