import { Inject, Injectable } from "@nestjs/common"; import { In, Not } from "typeorm"; import { CargoType } from "../rule-engine/entities/cargo-type.entity"; import { ContainerType } from "../rule-engine/entities/container-type.entity"; import { CARGO_TYPES_REPOSITORY, ICargoTypesRepository, } from "../rule-engine/interfaces/cargo-types.repository.interface"; import { CONTAINER_TYPES_REPOSITORY, IContainerTypesRepository, } from "../rule-engine/interfaces/container-types.repository.interface"; import { IServiceTypesRepository, SERVICE_TYPES_REPOSITORY, } from "../rule-engine/interfaces/service-types.repository.interface"; import { IShippingLinesRepository, SHIPPING_LINES_REPOSITORY, } from "../rule-engine/interfaces/shipping-lines.repository.interface"; import { IYardsRepository, YARDS_REPOSITORY, } from "../rule-engine/interfaces/yards.repository.interface"; import { BookingReferenceCargoTypeChildDto, BookingReferenceCargoTypeGroupDto, BookingReferenceContainerSizeGroupDto, BookingReferenceContainerTypeDto, BookingReferenceDataDto, BookingReferenceServiceDto, BookingReferenceShippingLineDto, BookingReferenceYardDto, } from "./dto/booking-reference-data.dto"; const LEGACY_YARD_CODES = ["LEGACY_ORIGIN", "LEGACY_DEST"] as const; export function buildCargoTypeTree( rows: CargoType[], ): BookingReferenceCargoTypeGroupDto[] { const active = rows.filter((r) => r.isActive); const parents = active .filter((r) => !r.parentGroupId) .sort( (a, b) => a.displayOrder - b.displayOrder || a.code.localeCompare(b.code), ); return parents.map((parent) => { const children = active .filter((r) => r.parentGroupId === parent.id) .sort( (a, b) => a.displayOrder - b.displayOrder || a.code.localeCompare(b.code), ) .map( (child): BookingReferenceCargoTypeChildDto => ({ id: child.id, name: child.cargoTypeName, code: child.code, unit_of_measure: child.unitOfMeasure ?? null, }), ); const group: BookingReferenceCargoTypeGroupDto = { id: parent.id, name: parent.cargoTypeName, code: parent.code, }; if (children.length > 0) { group.children = children; } return group; }); } export function groupContainersBySize( rows: ContainerType[], ): BookingReferenceContainerSizeGroupDto[] { const active = rows.filter((r) => r.isActive); const bySize = new Map(); for (const ct of active) { const sizeKey = ct.sizeFt != null && ct.sizeFt > 0 ? `${ct.sizeFt}ft` : "other"; const list = bySize.get(sizeKey) ?? []; list.push(ct); bySize.set(sizeKey, list); } const sortSizeKey = (key: string): number => { if (key === "other") return Number.MAX_SAFE_INTEGER; const n = parseInt(key, 10); return Number.isNaN(n) ? Number.MAX_SAFE_INTEGER - 1 : n; }; return [...bySize.entries()] .sort(([a], [b]) => sortSizeKey(a) - sortSizeKey(b)) .map(([size, types]) => ({ size, types: types .sort( (a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0) || a.code.localeCompare(b.code), ) .map( (ct): BookingReferenceContainerTypeDto => ({ id: ct.id, name: ct.label?.trim() ? ct.label : ct.code, code: ct.code, is_reefer: ct.isReefer ?? false, }), ), })); } @Injectable() export class BookingReferenceDataService { constructor( @Inject(YARDS_REPOSITORY) private readonly yardsRepository: IYardsRepository, @Inject(CONTAINER_TYPES_REPOSITORY) private readonly containerTypesRepository: IContainerTypesRepository, @Inject(SERVICE_TYPES_REPOSITORY) private readonly serviceTypesRepository: IServiceTypesRepository, @Inject(SHIPPING_LINES_REPOSITORY) private readonly shippingLinesRepository: IShippingLinesRepository, @Inject(CARGO_TYPES_REPOSITORY) private readonly cargoTypesRepository: ICargoTypesRepository, ) { } async getReferenceData(): Promise { const [yards, containerTypes, serviceTypes, shippingLines, cargoTypes] = await Promise.all([ this.yardsRepository.findAll({ where: { isActive: true, code: Not(In([...LEGACY_YARD_CODES])), }, order: { displayOrder: "ASC", code: "ASC" }, }), this.containerTypesRepository.findAll({ where: { isActive: true }, order: { displayOrder: "ASC", code: "ASC" }, }), this.serviceTypesRepository.findAll({ where: { isActive: true }, order: { displayOrder: "ASC", code: "ASC" }, }), this.shippingLinesRepository.findAll({ where: { isActive: true }, order: { label: "ASC", code: "ASC" }, }), this.cargoTypesRepository.findAll({ where: { isActive: true }, order: { displayOrder: "ASC", code: "ASC" }, }), ]); return { yard: yards.map( (y): BookingReferenceYardDto => ({ id: y.id, name: y.label, code: y.code, country: y.country, }), ), containers: groupContainersBySize(containerTypes), service: serviceTypes.map( (s): BookingReferenceServiceDto => ({ name: s.serviceName, ...s, }), ), shipping_line: shippingLines.map( (sl): BookingReferenceShippingLineDto => ({ id: sl.id, name: sl.label, code: sl.code, }), ), cargo_type: buildCargoTypeTree(cargoTypes), }; } }