xpeditis2.0/apps/backend/src/infrastructure/carriers/one/one.mapper.ts
David 4b00ee2601
Some checks failed
CI/CD Pipeline - Xpeditis PreProd / Frontend - Docker Build & Push (push) Blocked by required conditions
CI/CD Pipeline - Xpeditis PreProd / Deploy to PreProd Server (push) Blocked by required conditions
CI/CD Pipeline - Xpeditis PreProd / Run Smoke Tests (push) Blocked by required conditions
CI/CD Pipeline - Xpeditis PreProd / Backend - Build & Test (push) Failing after 5m53s
CI/CD Pipeline - Xpeditis PreProd / Backend - Docker Build & Push (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Frontend - Build & Test (push) Has been cancelled
fix: replace relative domain imports with TypeScript path aliases
- Replace all ../../domain/ imports with @domain/ across 67 files
- Configure NestJS to use tsconfig.build.json with rootDir
- Add tsc-alias to resolve path aliases after build
- This fixes 'Cannot find module' TypeScript compilation errors

Fixed files:
- 30 files in application layer
- 37 files in infrastructure layer
2025-11-16 19:20:58 +01:00

146 lines
4.6 KiB
TypeScript

/**
* ONE (Ocean Network Express) Request/Response Mapper
*/
import { Injectable } from '@nestjs/common';
import { CarrierRateSearchInput } from '@domain/ports/out/carrier-connector.port';
import { RateQuote, RouteSegment, Surcharge } from '@domain/entities/rate-quote.entity';
import { v4 as uuidv4 } from 'uuid';
@Injectable()
export class ONERequestMapper {
toONERequest(input: CarrierRateSearchInput): any {
return {
loading_port: input.origin,
discharge_port: input.destination,
equipment_type: this.mapContainerType(input.containerType),
cargo_cutoff_date: input.departureDate,
shipment_type: input.mode,
dangerous_cargo: input.isHazmat || false,
imo_class: input.imoClass,
cargo_weight_kg: input.weight,
cargo_volume_cbm: input.volume,
};
}
fromONEResponse(oneResponse: any, originalInput: CarrierRateSearchInput): RateQuote[] {
if (!oneResponse.instant_quotes || oneResponse.instant_quotes.length === 0) {
return [];
}
return oneResponse.instant_quotes.map((quote: any) => {
const surcharges: Surcharge[] = [];
// Parse surcharges
if (quote.additional_charges) {
for (const [key, value] of Object.entries(quote.additional_charges)) {
if (typeof value === 'number' && value > 0) {
surcharges.push({
type: key.toUpperCase(),
description: this.formatChargeName(key),
amount: value,
currency: quote.currency || 'USD',
});
}
}
}
const baseFreight = quote.ocean_freight || 0;
const totalSurcharges = surcharges.reduce((sum, s) => sum + s.amount, 0);
const totalAmount = baseFreight + totalSurcharges;
// Build route segments
const route: RouteSegment[] = [];
// Origin port
route.push({
portCode: originalInput.origin,
portName: quote.loading_port_name || originalInput.origin,
departure: new Date(quote.departure_date),
vesselName: quote.vessel_details?.name,
voyageNumber: quote.vessel_details?.voyage,
});
// Transshipment ports
if (quote.via_ports && Array.isArray(quote.via_ports)) {
quote.via_ports.forEach((port: any) => {
route.push({
portCode: port.code || port,
portName: port.name || port.code || port,
});
});
}
// Destination port
route.push({
portCode: originalInput.destination,
portName: quote.discharge_port_name || originalInput.destination,
arrival: new Date(quote.arrival_date),
});
const transitDays =
quote.transit_days || this.calculateTransitDays(quote.departure_date, quote.arrival_date);
return RateQuote.create({
id: uuidv4(),
carrierId: 'one',
carrierName: 'ONE',
carrierCode: 'ONEY',
origin: {
code: originalInput.origin,
name: quote.loading_port_name || originalInput.origin,
country: quote.loading_country || 'Unknown',
},
destination: {
code: originalInput.destination,
name: quote.discharge_port_name || originalInput.destination,
country: quote.discharge_country || 'Unknown',
},
pricing: {
baseFreight,
surcharges,
totalAmount,
currency: quote.currency || 'USD',
},
containerType: originalInput.containerType,
mode: (originalInput.mode as 'FCL' | 'LCL') || 'FCL',
etd: new Date(quote.departure_date),
eta: new Date(quote.arrival_date),
transitDays,
route,
availability: quote.capacity_status?.available || 0,
frequency: quote.service_info?.frequency || 'Weekly',
vesselType: 'Container Ship',
co2EmissionsKg: quote.environmental_info?.co2_emissions,
});
});
}
private mapContainerType(type: string): string {
const mapping: Record<string, string> = {
'20GP': '20DV',
'40GP': '40DV',
'40HC': '40HC',
'45HC': '45HC',
'20RF': '20RF',
'40RF': '40RH',
};
return mapping[type] || type;
}
private formatChargeName(key: string): string {
return key
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
private calculateTransitDays(departure?: string, arrival?: string): number {
if (!departure || !arrival) return 0;
const depDate = new Date(departure);
const arrDate = new Date(arrival);
const diff = arrDate.getTime() - depDate.getTime();
return Math.ceil(diff / (1000 * 60 * 60 * 24));
}
}