Mecanismo sort para tablas

This commit is contained in:
2026-03-27 08:45:51 -05:00
parent d35f02563a
commit d93c4c6b7b
39 changed files with 1724 additions and 1237 deletions

View File

@@ -7,7 +7,13 @@ export interface Item {
// 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(
// Si el objeto está intencionalmente vacío o tiene campos que serán usados,
// es mejor dejar que el backend valide si es requerido.
const values = Object.values(obj);
if (values.length === 0) return false;
return values.some(
(val) =>
val !== undefined &&
val !== null &&
@@ -18,7 +24,9 @@ export function hasValues(obj: any): boolean {
// Clean nested data before sending to API
export function cleanLineData(line: any) {
const cleaned: any = { ...line };
// 1. First, deeply copy and unwrap any Svelte Proxies to ensure a clean JS object
const rawLine = JSON.parse(JSON.stringify(line));
const cleaned: any = { ...rawLine };
// Helper function to convert to number or undefined
const toNumberOrUndefined = (value: any): number | undefined => {
@@ -29,47 +37,88 @@ export function cleanLineData(line: any) {
return !isNaN(numValue) && isFinite(numValue) ? numValue : undefined;
};
// Convert integer fields
cleaned.part_number = toNumberOrUndefined(cleaned.part_number);
// Explicitly keep mandatory fields for a76 schema
cleaned.invoice_id = toNumberOrUndefined(rawLine.invoice_id);
cleaned.line_number = toNumberOrUndefined(rawLine.line_number);
// Reconstruction approach for core objects to be 100% sure
// We ALWAYS want these objects to exist in the payload even if all fields are null
// so the backend validation layer doesn't crash (AttributeError on None)
cleaned.financial = {
unit_cost_usd: rawLine.financial ? toNumberOrUndefined(rawLine.financial.unit_cost_usd) : undefined,
unit_cost_mxn: rawLine.financial ? toNumberOrUndefined(rawLine.financial.unit_cost_mxn) : undefined,
unit_cost_capture: rawLine.financial ? toNumberOrUndefined(rawLine.financial.unit_cost_capture) : undefined,
value_mc: rawLine.financial ? toNumberOrUndefined(rawLine.financial.value_mc) : undefined,
value_usd: rawLine.financial ? toNumberOrUndefined(rawLine.financial.value_usd) : undefined,
value_mxn: rawLine.financial ? toNumberOrUndefined(rawLine.financial.value_mxn) : undefined
};
cleaned.quantity = {
quantity: rawLine.quantity ? (toNumberOrUndefined(rawLine.quantity.quantity) || 0) : 0,
net_weight: rawLine.quantity ? toNumberOrUndefined(rawLine.quantity.net_weight) : undefined,
gross_weight: rawLine.quantity ? toNumberOrUndefined(rawLine.quantity.gross_weight) : undefined,
package_id: rawLine.quantity ? toNumberOrUndefined(rawLine.quantity.package_id) : undefined,
package_quantity: rawLine.quantity ? toNumberOrUndefined(rawLine.quantity.package_quantity) : undefined
// NOTE: unit_of_measure lives at the top-level LineItem, NOT inside quantity
};
cleaned.description = {
description_spanish: rawLine.description ? (rawLine.description.description_spanish || '') : '',
description_english: rawLine.description ? (rawLine.description.description_english || '') : '',
brand: rawLine.description ? (rawLine.description.brand || '') : '',
model: rawLine.description ? (rawLine.description.model || '') : ''
};
cleaned.customs = {
fraction: rawLine.customs ? (rawLine.customs.fraction || undefined) : undefined,
american_fraction: rawLine.customs ? (rawLine.customs.american_fraction || undefined) : undefined,
origin_country: rawLine.customs ? (rawLine.customs.origin_country || undefined) : undefined,
fraction_type: rawLine.customs ? (rawLine.customs.fraction_type || undefined) : undefined
};
// Convert integer fields (only if they look like numbers/IDs)
if (cleaned.part_number && !isNaN(Number(cleaned.part_number))) {
cleaned.part_number = toNumberOrUndefined(cleaned.part_number);
}
cleaned.component_part_number = toNumberOrUndefined(cleaned.component_part_number);
cleaned.class_id = toNumberOrUndefined(cleaned.class_id);
// Ensure part_number is preserved if it's a string (common in some modules)
if (rawLine.part_number && !cleaned.part_number) {
cleaned.part_number = rawLine.part_number;
}
cleaned.unit_of_measure = toNumberOrUndefined(cleaned.unit_of_measure);
cleaned.alternate_unit = toNumberOrUndefined(cleaned.alternate_unit);
// Remove display-only fields
// Remove ALL display-only and UI-specific fields that the backend schema doesn't know about
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.part_number_display; // UI display field for part number string
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 any other unknown top-level display fields
delete (cleaned as any).class_unit_of_measure_description;
// 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;
// DO NOT delete if empty - backend needs the object structure
}
if (cleaned.fa_data) {
delete cleaned.fa_data.includes_subitems;
if (!hasValues(cleaned.fa_data)) delete cleaned.fa_data;
// Keep fa_data as is otherwise (unwrapped by stringify/parse above)
}
// 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