import { ExcelRow } from '../types'; export interface MappingFieldPreview { sourceLabel: string; sourceIndex: number | null; targetField: string; value: any; } export interface BusinessCentralMappingPreview { articleNo: string; itemsPayload: Record; itemUnitsOfMeasurePayload: Record; itemsFields: MappingFieldPreview[]; itemUnitsFields: MappingFieldPreview[]; } function findHeaderIndex(headers: string[], patterns: string[]): number { const normalized = headers.map(h => String(h || '').toLowerCase()); return normalized.findIndex(header => patterns.every(pattern => header.includes(pattern.toLowerCase())) ); } function formatDateForBc(value: any): string | null { if (value === null || value === undefined || value === '') return null; if (typeof value === 'number' && value >= 25569 && value <= 60000) { const excelEpoch = new Date(1899, 11, 30); const date = new Date(excelEpoch.getTime() + value * 86400000); return date.toISOString().split('T')[0]; } const str = String(value).trim(); if (!str) return null; if (/^\d{4}-\d{2}-\d{2}$/.test(str)) return str; const parsed = new Date(str); if (!Number.isNaN(parsed.getTime())) { return parsed.toISOString().split('T')[0]; } return str; } function getValue(row: ExcelRow, index: number | null): any { if (index === null || index < 0) return null; const value = row[index]; return value === undefined || value === '' ? null : value; } function toBcDecimal(value: any): number | null { if (value === null || value === undefined || value === '') return null; if (typeof value === 'number' && Number.isFinite(value)) return value; const normalized = String(value).trim().replace(/\s+/g, '').replace(',', '.'); if (!normalized) return null; const parsed = Number(normalized); return Number.isFinite(parsed) ? parsed : null; } function makeFieldPreview( sourceLabel: string, sourceIndex: number | null, targetField: string, value: any ): MappingFieldPreview { return { sourceLabel, sourceIndex, targetField, value }; } export function buildBusinessCentralMappingPreview(headers: string[], row: ExcelRow): BusinessCentralMappingPreview { const articleNoIdx = findHeaderIndex(headers, ['article', 'no']); const articleDetailsEnIdx = findHeaderIndex(headers, ['article', 'details', 'english']); const articleDetailsDeIdx = findHeaderIndex(headers, ['article', 'details', 'german']); const launchDateIdx = findHeaderIndex(headers, ['launch']); const readyToOrderDateIdx = findHeaderIndex(headers, ['ready']); const moqIdx = findHeaderIndex(headers, ['moq']); const shortDeIdx = findHeaderIndex(headers, ['short', 'description', 'german']); const shortEnIdx = findHeaderIndex(headers, ['short', 'description', 'english']); const cpnpIdx = findHeaderIndex(headers, ['cpnp']); const unitsOuterIdx = findHeaderIndex(headers, ['units', 'outer']); const innerWIdx = findHeaderIndex(headers, ['inner', 'w']); const innerLIdx = findHeaderIndex(headers, ['inner', 'l']); const innerHIdx = findHeaderIndex(headers, ['inner', 'h']); const outerWIdx = findHeaderIndex(headers, ['outer', 'w']); const outerLIdx = findHeaderIndex(headers, ['outer', 'l']); const outerHIdx = findHeaderIndex(headers, ['outer', 'h']); const articleNo = String(getValue(row, articleNoIdx) ?? ''); const itemsPayload = { no: getValue(row, articleNoIdx), articleDetailsEnglish: getValue(row, articleDetailsEnIdx), articleDetailsGerman: getValue(row, articleDetailsDeIdx), launchDate: formatDateForBc(getValue(row, launchDateIdx)), readyToOrderDate: formatDateForBc(getValue(row, readyToOrderDateIdx)), minimumOrderQuantity: toBcDecimal(getValue(row, moqIdx)), shortDescriptionInGerman: getValue(row, shortDeIdx), shortDescriptionInEnglish: getValue(row, shortEnIdx), cpnpNo: getValue(row, cpnpIdx), }; const itemUnitsOfMeasurePayload = { itemNo: getValue(row, articleNoIdx), qtyPerUnitOfMeasure6: toBcDecimal(getValue(row, unitsOuterIdx)), width4: toBcDecimal(getValue(row, innerWIdx)), length4: toBcDecimal(getValue(row, innerLIdx)), height4: toBcDecimal(getValue(row, innerHIdx)), width6: toBcDecimal(getValue(row, outerWIdx)), length6: toBcDecimal(getValue(row, outerLIdx)), height6: toBcDecimal(getValue(row, outerHIdx)), }; return { articleNo, itemsPayload, itemUnitsOfMeasurePayload, itemsFields: [ makeFieldPreview('Article No.', articleNoIdx, 'no', itemsPayload.no), makeFieldPreview('Article Details - English', articleDetailsEnIdx, 'articleDetailsEnglish', itemsPayload.articleDetailsEnglish), makeFieldPreview('Article Details - German', articleDetailsDeIdx, 'articleDetailsGerman', itemsPayload.articleDetailsGerman), makeFieldPreview('Launch Date', launchDateIdx, 'launchDate', itemsPayload.launchDate), makeFieldPreview('Ready to Order Date', readyToOrderDateIdx, 'readyToOrderDate', itemsPayload.readyToOrderDate), makeFieldPreview('MOQ', moqIdx, 'minimumOrderQuantity', itemsPayload.minimumOrderQuantity), makeFieldPreview('Short Description - German', shortDeIdx, 'shortDescriptionInGerman', itemsPayload.shortDescriptionInGerman), makeFieldPreview('Short Description - English', shortEnIdx, 'shortDescriptionInEnglish', itemsPayload.shortDescriptionInEnglish), makeFieldPreview('CPNP', cpnpIdx, 'cpnpNo', itemsPayload.cpnpNo), ], itemUnitsFields: [ makeFieldPreview('Article No.', articleNoIdx, 'itemNo', itemUnitsOfMeasurePayload.itemNo), makeFieldPreview('Units Outer', unitsOuterIdx, 'qtyPerUnitOfMeasure6', itemUnitsOfMeasurePayload.qtyPerUnitOfMeasure6), makeFieldPreview('CDU/Inner W (cm)', innerWIdx, 'width4', itemUnitsOfMeasurePayload.width4), makeFieldPreview('CDU/Inner L (cm)', innerLIdx, 'length4', itemUnitsOfMeasurePayload.length4), makeFieldPreview('CDU/Inner H (cm)', innerHIdx, 'height4', itemUnitsOfMeasurePayload.height4), makeFieldPreview('Outer W (cm)', outerWIdx, 'width6', itemUnitsOfMeasurePayload.width6), makeFieldPreview('Outer L (cm)', outerLIdx, 'length6', itemUnitsOfMeasurePayload.length6), makeFieldPreview('Outer H (cm)', outerHIdx, 'height6', itemUnitsOfMeasurePayload.height6), ], }; }