feat(data): update sell-out parser mapping and validation for new structure

This commit is contained in:
Christian Vidal Wolf
2026-02-25 11:46:02 +01:00
parent 27d4268aff
commit ce2241a5ec
+38 -4
View File
@@ -270,8 +270,32 @@ const isAllowedCustomer = (customer: string): boolean => {
// --- SALES / SELL OUT MAPPING --- // --- 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 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']); const yearStr = getColumnValue(row, ['YEAR', 'Year', 'D']);
// Sanitize year string before parsing (remove commas/dots e.g. "2,023") // Sanitize year string before parsing (remove commas/dots e.g. "2,023")
let year = parseInt(yearStr.replace(/[,.]/g, '')) || 0; 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 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 title = getColumnValue(row, ['ARTICLE NAME (Customer)', 'ARTICLE NAME (Craze)', 'Title', 'TITLE', 'Product Title', 'Article Name']);
const articleName = getColumnValue(row, ['ARTICLE NAME (Craze)', 'Article Name', 'ArticleName', 'Title']); const articleName = getColumnValue(row, ['ARTICLE NAME (Craze)', 'ARTICLE NAME (Customer)', 'Article Name', 'ArticleName', 'Title']);
const unitsRaw = getColumnValue(row, ['UNITS', 'Units', 'Quantity', 'Qty']); 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 { return {
id: `row-${index}`, id: `row-${index}`,
@@ -329,6 +353,12 @@ export const processCSV = (fileOrContent: File | string): Promise<SalesRecord[]>
skipEmptyLines: true, skipEmptyLines: true,
complete: (results: any) => { complete: (results: any) => {
try { 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) => { const data: SalesRecord[] = results.data.map((row: any, index: number) => {
return mapRowToRecord(row, index); return mapRowToRecord(row, index);
}) })
@@ -353,6 +383,10 @@ export const processExcel = async (file: File): Promise<SalesRecord[]> => {
const worksheet = workbook.Sheets[firstSheetName]; const worksheet = workbook.Sheets[firstSheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: "" }); 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) => { const data: SalesRecord[] = jsonData.map((row: any, index: number) => {
return mapRowToRecord(row, index); return mapRowToRecord(row, index);
}) })