diff --git a/services/dataProcessor.ts b/services/dataProcessor.ts index 3a0b1c5..0009bca 100644 --- a/services/dataProcessor.ts +++ b/services/dataProcessor.ts @@ -270,8 +270,32 @@ const isAllowedCustomer = (customer: string): boolean => { // --- SALES / SELL OUT MAPPING --- +export const validateSellOutHeaders = (headers: string[]) => { + const normHeaders = headers.map(h => String(h).trim().toLowerCase()); + + const hasYear = normHeaders.includes('year'); + const hasTime = normHeaders.includes('month') || normHeaders.includes('week'); + const hasCustomerRef = normHeaders.includes('customer reference') || normHeaders.includes('asin'); + const hasEan = normHeaders.includes('ean'); + const hasUnits = normHeaders.includes('units'); + const hasAmount = normHeaders.includes('amount_eur') || normHeaders.includes('amount'); + + const missing = []; + if (!hasYear) missing.push('YEAR'); + if (!hasTime) missing.push('MONTH or WEEK'); + if (!hasCustomerRef) missing.push('CUSTOMER REFERENCE'); + if (!hasEan) missing.push('EAN'); + if (!hasUnits) missing.push('UNITS'); + if (!hasAmount) missing.push('AMOUNT_EUR'); + + if (missing.length > 0) { + throw new Error(`Invalid or missing critical columns in Sell-Out Report. Missing: ${missing.join(', ')}`); + } +}; + const mapRowToRecord = (row: any, index: number): SalesRecord => { - const customer = getColumnValue(row, ['NEW CUSTOMER', 'Customer', 'Client', 'Account', 'Partner', 'COUNTRY', 'Country', 'Market']) || 'Unknown'; + // Add exact matches to the front of getColumnValue arrays + const customer = getColumnValue(row, ['NEW CUSTOMER', 'COUNTRY', 'Customer', 'Client', 'Account', 'Partner', 'Country', 'Market']) || 'Unknown'; const yearStr = getColumnValue(row, ['YEAR', 'Year', 'D']); // Sanitize year string before parsing (remove commas/dots e.g. "2,023") let year = parseInt(yearStr.replace(/[,.]/g, '')) || 0; @@ -299,11 +323,11 @@ const mapRowToRecord = (row: any, index: number): SalesRecord => { ]); const sku = getColumnValue(row, ['RAW ARTICLE NO.', 'SKU', 'Sku', 'Item No']); - const title = getColumnValue(row, ['ARTICLE NAME (Craze)', 'Title', 'TITLE', 'Product Title', 'Article Name', 'ArticleName']); - const articleName = getColumnValue(row, ['ARTICLE NAME (Craze)', 'Article Name', 'ArticleName', 'Title']); + const title = getColumnValue(row, ['ARTICLE NAME (Customer)', 'ARTICLE NAME (Craze)', 'Title', 'TITLE', 'Product Title', 'Article Name']); + const articleName = getColumnValue(row, ['ARTICLE NAME (Craze)', 'ARTICLE NAME (Customer)', 'Article Name', 'ArticleName', 'Title']); const unitsRaw = getColumnValue(row, ['UNITS', 'Units', 'Quantity', 'Qty']); - const sellOutRaw = getColumnValue(row, ['AMOUNT', 'Sell Out', 'SellOut', 'Revenue', 'Sales', 'Turnover']); + const sellOutRaw = getColumnValue(row, ['AMOUNT_EUR', 'AMOUNT', 'Sell Out', 'SellOut', 'Revenue', 'Sales', 'Turnover']); return { id: `row-${index}`, @@ -329,6 +353,12 @@ export const processCSV = (fileOrContent: File | string): Promise skipEmptyLines: true, complete: (results: any) => { try { + if (results.meta && results.meta.fields) { + validateSellOutHeaders(results.meta.fields); + } else if (results.data && results.data.length > 0) { + validateSellOutHeaders(Object.keys(results.data[0])); + } + const data: SalesRecord[] = results.data.map((row: any, index: number) => { return mapRowToRecord(row, index); }) @@ -353,6 +383,10 @@ export const processExcel = async (file: File): Promise => { const worksheet = workbook.Sheets[firstSheetName]; const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: "" }); + if (jsonData.length > 0) { + validateSellOutHeaders(Object.keys(jsonData[0] as object)); + } + const data: SalesRecord[] = jsonData.map((row: any, index: number) => { return mapRowToRecord(row, index); })