export interface Item { id?: number; [key: string]: any; } // Helper function to check if an object has any meaningful values export function hasValues(obj: any): boolean { if (!obj || typeof obj !== 'object') return false; return Object.values(obj).some( (val) => val !== undefined && val !== null && val !== '' && !(typeof val === 'object' && !hasValues(val)) ); } // Clean nested data before sending to API export function cleanLineData(line: any) { const cleaned: any = { ...line }; // Helper function to convert to number or undefined const toNumberOrUndefined = (value: any): number | undefined => { if (value === undefined || value === null || value === '') { return undefined; } const numValue = Number(value); return !isNaN(numValue) && isFinite(numValue) ? numValue : undefined; }; // Convert integer fields cleaned.part_number = toNumberOrUndefined(cleaned.part_number); cleaned.component_part_number = toNumberOrUndefined(cleaned.component_part_number); cleaned.class_id = toNumberOrUndefined(cleaned.class_id); cleaned.unit_of_measure = toNumberOrUndefined(cleaned.unit_of_measure); cleaned.alternate_unit = toNumberOrUndefined(cleaned.alternate_unit); // Remove display-only fields delete cleaned.class_code; delete cleaned.class_unit_of_measure; delete cleaned.class_description; // UI specific fields that shouldn't be in the payload delete cleaned.part_description_es; delete cleaned.part_description_en; delete cleaned.unit_code; delete cleaned.unit_description; delete cleaned.includes_subitems; delete cleaned.payment_method_description; // Only delete part_number if it's the string code from UI, but schema expects int ID. // In this codebase, if part_number is populated from existing data, it's an ID. // If it's a new item, it might be cleaned. // Remove display-only fields from nested objects if (cleaned.customs) { delete cleaned.customs.origin_country_name; delete cleaned.customs.fraction_description; if (!hasValues(cleaned.customs)) delete cleaned.customs; } if (cleaned.fa_data) { delete cleaned.fa_data.includes_subitems; if (!hasValues(cleaned.fa_data)) delete cleaned.fa_data; } // Remove empty nested objects if (cleaned.financial && !hasValues(cleaned.financial)) delete cleaned.financial; if (cleaned.quantity && !hasValues(cleaned.quantity)) delete cleaned.quantity; if (cleaned.description && !hasValues(cleaned.description)) delete cleaned.description; if (cleaned.reference && !hasValues(cleaned.reference)) delete cleaned.reference; if (cleaned.series != null) { const arr = Array.isArray(cleaned.series) ? cleaned.series : [cleaned.series]; cleaned.series = arr .map((s: any) => { if (!s || typeof s !== 'object') return null; const { id, line_item_id, ...rest } = s; return hasValues(rest) ? rest : null; }) .filter((s: any) => s != null); if (cleaned.series.length === 0) delete cleaned.series; } return cleaned; } // Normalize numeric values from strings to numbers (for editing) export function normalizeItemData(item: Partial): Partial { if (item) { const normalizedItem = { ...item }; // Normalize financials if (normalizedItem.financial) { const f = normalizedItem.financial; normalizedItem.financial = { ...f, unit_cost_usd: f.unit_cost_usd != null ? Number(f.unit_cost_usd) : undefined, unit_cost_mxn: f.unit_cost_mxn != null ? Number(f.unit_cost_mxn) : undefined, value_usd: f.value_usd != null ? Number(f.value_usd) : undefined, value_mxn: f.value_mxn != null ? Number(f.value_mxn) : undefined }; } // Normalize quantities if (normalizedItem.quantity) { const q = normalizedItem.quantity; normalizedItem.quantity = { ...q, quantity: q.quantity != null ? Number(q.quantity) : undefined, net_weight: q.net_weight != null ? Number(q.net_weight) : undefined, gross_weight: q.gross_weight != null ? Number(q.gross_weight) : undefined, package_quantity: q.package_quantity != null ? Number(q.package_quantity) : undefined }; } return normalizedItem; } return item; }