import { Injectable } from '@nestjs/common'; import { CsvRate } from '@domain/entities/csv-rate.entity'; import { Volume } from '@domain/value-objects/volume.vo'; import { CsvRateResultDto, CsvRateSearchResponseDto } from '../dto/csv-rate-search.dto'; import { CsvRateSearchInput, CsvRateSearchOutput, CsvRateSearchResult, RateSearchFilters, } from '@domain/ports/in/search-csv-rates.port'; import { RateSearchFiltersDto } from '../dto/rate-search-filters.dto'; import { CsvRateConfigDto } from '../dto/csv-rate-upload.dto'; import { CsvRateConfigOrmEntity } from '@infrastructure/persistence/typeorm/entities/csv-rate-config.orm-entity'; /** * CSV Rate Mapper * * Maps between domain entities and DTOs * Follows hexagonal architecture principles */ @Injectable() export class CsvRateMapper { /** * Map DTO filters to domain filters */ mapFiltersDtoToDomain(dto?: RateSearchFiltersDto): RateSearchFilters | undefined { if (!dto) { return undefined; } return { companies: dto.companies, minVolumeCBM: dto.minVolumeCBM, maxVolumeCBM: dto.maxVolumeCBM, minWeightKG: dto.minWeightKG, maxWeightKG: dto.maxWeightKG, palletCount: dto.palletCount, minPrice: dto.minPrice, maxPrice: dto.maxPrice, currency: dto.currency, minTransitDays: dto.minTransitDays, maxTransitDays: dto.maxTransitDays, containerTypes: dto.containerTypes, onlyAllInPrices: dto.onlyAllInPrices, departureDate: dto.departureDate ? new Date(dto.departureDate) : undefined, }; } /** * Map domain search result to DTO */ mapSearchResultToDto(result: CsvRateSearchResult): CsvRateResultDto { const rate = result.rate; return { companyName: rate.companyName, companyEmail: rate.companyEmail, origin: rate.origin.getValue(), destination: rate.destination.getValue(), containerType: rate.containerType.getValue(), priceUSD: result.calculatedPrice.usd, priceEUR: result.calculatedPrice.eur, primaryCurrency: result.calculatedPrice.primaryCurrency, priceBreakdown: { basePrice: result.priceBreakdown.basePrice, volumeCharge: result.priceBreakdown.volumeCharge, weightCharge: result.priceBreakdown.weightCharge, palletCharge: result.priceBreakdown.palletCharge, surcharges: result.priceBreakdown.surcharges.map(s => ({ code: s.code, description: s.description, amount: s.amount, type: s.type, })), totalSurcharges: result.priceBreakdown.totalSurcharges, totalPrice: result.priceBreakdown.totalPrice, currency: result.priceBreakdown.currency, }, hasSurcharges: rate.hasSurcharges(), surchargeDetails: rate.hasSurcharges() ? rate.getSurchargeDetails() : null, transitDays: rate.transitDays, validUntil: rate.validity.getEndDate().toISOString().split('T')[0], source: result.source, matchScore: result.matchScore, }; } /** * Map domain search output to response DTO */ mapSearchOutputToResponseDto(output: CsvRateSearchOutput): CsvRateSearchResponseDto { return { results: output.results.map(result => this.mapSearchResultToDto(result)), totalResults: output.totalResults, searchedFiles: output.searchedFiles, searchedAt: output.searchedAt, appliedFilters: output.appliedFilters as any, // Already matches DTO structure }; } /** * Map ORM entity to DTO */ mapConfigEntityToDto(entity: CsvRateConfigOrmEntity): CsvRateConfigDto { return { id: entity.id, companyName: entity.companyName, csvFilePath: entity.csvFilePath, type: entity.type, hasApi: entity.hasApi, apiConnector: entity.apiConnector, isActive: entity.isActive, uploadedAt: entity.uploadedAt, rowCount: entity.rowCount, metadata: entity.metadata, }; } /** * Map multiple config entities to DTOs */ mapConfigEntitiesToDtos(entities: CsvRateConfigOrmEntity[]): CsvRateConfigDto[] { return entities.map(entity => this.mapConfigEntityToDto(entity)); } }