import type { ContainerPlacementInput } from './utils/wagon-plan.util'; export type ContainerUnitForPlacement = { bookingId: string; bookingReference?: string | null; bookingContainerId: string; unitIndex: number; label: string; teuSlots?: number; sizeFt?: number; containerNumber?: string | null; }; export function placeholderContainerNumber(unit: ContainerUnitForPlacement): string { const ref = unit.bookingReference ?? unit.bookingId.slice(0, 8); return `TBD-${ref}-${unit.unitIndex + 1}`; } export function isPlaceholderContainerNumber(value: string | null | undefined): boolean { return Boolean(value?.trim().startsWith('TBD-')); } export function resolveContainerNumber(unit: ContainerUnitForPlacement): string { const trimmed = unit.containerNumber?.trim(); return trimmed || placeholderContainerNumber(unit); } export function autoFillPlacements( units: ContainerUnitForPlacement[], containerSlots: number[], ): ContainerPlacementInput[] { if (!units.length || !containerSlots.length) return []; const placements: ContainerPlacementInput[] = []; const MAX_TEU_PER_WAGON = 2; let currentSlotIndex = 0; let teuInCurrentSlot = 0; for (const unit of units) { const teu = unit.teuSlots ?? (unit.sizeFt && unit.sizeFt >= 40 ? 2 : 1); if (teuInCurrentSlot > 0 && teuInCurrentSlot + teu > MAX_TEU_PER_WAGON) { currentSlotIndex += 1; teuInCurrentSlot = 0; } const sequenceNo = containerSlots[Math.min(currentSlotIndex, containerSlots.length - 1)] ?? containerSlots[containerSlots.length - 1] ?? containerSlots[0]; placements.push({ bookingContainerId: unit.bookingContainerId, unitIndex: unit.unitIndex, sequenceNo, containerNumber: resolveContainerNumber(unit), }); teuInCurrentSlot += teu; } return placements; } export function findMissingContainerNumberIssues( units: ContainerUnitForPlacement[], placements: ContainerPlacementInput[], ): Array<{ bookingId: string; issue: string }> { const issues: Array<{ bookingId: string; issue: string }> = []; const byUnit = new Map( placements.map((p) => [`${p.bookingContainerId}:${p.unitIndex}`, p]), ); for (const unit of units) { const placement = byUnit.get(`${unit.bookingContainerId}:${unit.unitIndex}`); if (!placement?.containerNumber?.trim()) { issues.push({ bookingId: unit.bookingId, issue: `Missing container number for ${unit.label}`, }); } } return issues; } export function placementsForBookings( placements: ContainerPlacementInput[], bookingIds: Set, units: ContainerUnitForPlacement[], ): ContainerPlacementInput[] { const unitBookingIds = new Map( units.map((u) => [`${u.bookingContainerId}:${u.unitIndex}`, u.bookingId]), ); return placements.filter((p) => { const bookingId = unitBookingIds.get(`${p.bookingContainerId}:${p.unitIndex}`); return bookingId ? bookingIds.has(bookingId) : false; }); }