fix/carga-csv-headers
This commit is contained in:
@@ -49,6 +49,168 @@ from api.v1.modules.a76.layouts_csv.transportistas.template_config import (
|
||||
)
|
||||
|
||||
|
||||
def _normalize_header_for_match(header: str) -> str:
|
||||
"""Normaliza cabeceras para comparación interna (sin alterar salida)."""
|
||||
if not header:
|
||||
return ""
|
||||
cleaned = header.strip()
|
||||
if cleaned.startswith("* "):
|
||||
cleaned = cleaned[2:]
|
||||
return cleaned.strip().upper()
|
||||
|
||||
|
||||
ALWAYS_REQUIRED_HEADERS_BY_TEMPLATE: Dict[str, set[str]] = {
|
||||
# Facturas encabezados
|
||||
"imp_temp_header": {
|
||||
"NUMERO FACTURA",
|
||||
"FECHA FACTURA",
|
||||
"REGIMEN",
|
||||
"CLAVE PROVEEDOR",
|
||||
"CLAVE VENDIDO A",
|
||||
"CLAVE ENVIADO A",
|
||||
"AGENTE ADUANAL",
|
||||
"ADUANA DE CRUCE",
|
||||
},
|
||||
"imp_def_header": {
|
||||
"NUMERO FACTURA",
|
||||
"FECHA FACTURA",
|
||||
"REGIMEN",
|
||||
"CLAVE PROVEEDOR",
|
||||
"CLAVE VENDIDO A",
|
||||
"CLAVE ENVIADO A",
|
||||
"AGENTE ADUANAL",
|
||||
},
|
||||
"exp_def_header": {
|
||||
"NUMERO FACTURA",
|
||||
"FECHA FACTURA",
|
||||
"REGIMEN",
|
||||
"CLAVE PROVEEDOR",
|
||||
"CLAVE VENDIDO A",
|
||||
"CLAVE ENVIADO A",
|
||||
"AGENTE ADUANAL",
|
||||
"ADUANA DE CRUCE",
|
||||
},
|
||||
"cmex_header": {
|
||||
"NUMERO FACTURA",
|
||||
"FECHA FACTURA",
|
||||
"CLAVE PROVEEDOR",
|
||||
"CLAVE VENDIDO A",
|
||||
"CLAVE ENVIADO A",
|
||||
},
|
||||
# Facturas partidas (plantillas del registry)
|
||||
"imp_temp_details": {
|
||||
"NUMERO FACTURA",
|
||||
"CLASE",
|
||||
"CANTIDAD IMPORTADA",
|
||||
"COSTO UNITARIO",
|
||||
"PESO NETO",
|
||||
"PAIS ORIGEN",
|
||||
"PREFERENCIA ARANCELARIA",
|
||||
},
|
||||
"imp_def_details": {
|
||||
"NUMERO FACTURA",
|
||||
"CLASE",
|
||||
"CANTIDAD IMPORTADA",
|
||||
"COSTO UNITARIO",
|
||||
"PESO NETO",
|
||||
"PAIS ORIGEN",
|
||||
"PREFERENCIA ARANCELARIA",
|
||||
"NUM. PARTE",
|
||||
},
|
||||
"exp_def_details": {
|
||||
"NUMERO FACTURA",
|
||||
"CLASE",
|
||||
"CANTIDAD IMPORTADA",
|
||||
"COSTO UNITARIO",
|
||||
"PESO NETO",
|
||||
"PAIS ORIGEN",
|
||||
"PREFERENCIA ARANCELARIA",
|
||||
},
|
||||
"cmex_details": {
|
||||
"NUMERO FACTURA",
|
||||
"CLASE",
|
||||
"CANTIDAD IMPORTADA",
|
||||
"COSTO UNITARIO",
|
||||
"PESO NETO",
|
||||
"PAIS ORIGEN",
|
||||
"PREFERENCIA ARANCELARIA",
|
||||
"NUM. PARTE",
|
||||
},
|
||||
# Facturas series (plantillas del registry)
|
||||
"imp_temp_series": {"NUMERO FACTURA", "LINEA FACTURA"},
|
||||
"imp_def_series": {"NUMERO FACTURA", "LINEA FACTURA"},
|
||||
"cmex_series": {"NUMERO FACTURA", "LINEA FACTURA"},
|
||||
# Catálogos y transportes
|
||||
"customs_brokers": {"TIPO", "CLAVE", "NOMBRE"},
|
||||
"clients_providers": {"PROCEDENCIA", "SHORT_NAME", "NOMBRE", "RFC"},
|
||||
"exchange_rates": {"FECHA", "VALOR"},
|
||||
"american_fractions": {"FRACCION_ARANCELARIA", "DESCRIPCION"},
|
||||
"material_classes": {
|
||||
"CLAVE CLASE",
|
||||
"CLASE",
|
||||
"DESCRIPCION ESPAÑOL",
|
||||
"DESCRIPCIONE",
|
||||
"TIPO DE MATERIAL",
|
||||
"CLAVEMAT",
|
||||
"U.M. COMERCIAL",
|
||||
"UNIMED",
|
||||
"FRACCION ARANCELARIA",
|
||||
"FRACCION",
|
||||
},
|
||||
"part_numbers": {
|
||||
"NUMERO DE PARTE",
|
||||
"NUMPARTE",
|
||||
"DESCRIPCION EN ESPAÑOL",
|
||||
"DESCRIPCIONE",
|
||||
"UNIDAD DE MEDIDA COMERCIAL",
|
||||
"UNIMED",
|
||||
},
|
||||
"items": {
|
||||
"NUMERO DE PARTE",
|
||||
"NUMPARTE",
|
||||
"DESCRIPCION EN ESPAÑOL",
|
||||
"DESCRIPCIONE",
|
||||
"UNIDAD DE MEDIDA COMERCIAL",
|
||||
"UNIMED",
|
||||
},
|
||||
"boms": {"NUMPARTE_PADRE", "NUMPARTE_COMPONENTE", "CANTIDAD"},
|
||||
"pedimentos": {
|
||||
"AÑO", "PATENTE", "NUMERO", "PEDIMENTO",
|
||||
"TIPO_OPERACION",
|
||||
"CLAVE_PEDIMENTO",
|
||||
"REGIMEN",
|
||||
"FECHA_INICIO",
|
||||
"FECHA_FINAL",
|
||||
"FECHA_PAGO",
|
||||
"ADUANA_SECCION_CRUCE",
|
||||
},
|
||||
"transports": {"CLAVE", "CODIGO DE ENTIDAD"},
|
||||
"drivers": {"TRANSPORTISTA", "LINEA", "CLAVE CONDUCTOR"},
|
||||
"trailers": {"NUMERO TRAILER"},
|
||||
"transporters": {"CLAVE TRANSPORTISTA", "NOMBRE"},
|
||||
}
|
||||
|
||||
|
||||
def _apply_required_prefix(template_id: str, headers: List[str]) -> List[str]:
|
||||
"""Prefija '* ' a cabeceras siempre obligatorias para la plantilla."""
|
||||
required_headers = ALWAYS_REQUIRED_HEADERS_BY_TEMPLATE.get(template_id)
|
||||
if not required_headers:
|
||||
return headers
|
||||
|
||||
required_norm = {_normalize_header_for_match(h) for h in required_headers}
|
||||
out: List[str] = []
|
||||
for header in headers:
|
||||
norm_header = _normalize_header_for_match(header)
|
||||
if norm_header and norm_header in required_norm:
|
||||
if header.startswith("* "):
|
||||
out.append(header)
|
||||
else:
|
||||
out.append(f"* {header}")
|
||||
else:
|
||||
out.append(header)
|
||||
return out
|
||||
|
||||
|
||||
def _canonicals_from_columns(cols: Optional[List[Dict]]) -> List[str]:
|
||||
"""Extrae la lista de nombres canónicos en orden a partir de una lista de columnas."""
|
||||
if not cols:
|
||||
@@ -163,7 +325,10 @@ TEMPLATE_FILENAMES: Dict[str, str] = {
|
||||
|
||||
def get_template_headers(template_id: str) -> Optional[List[str]]:
|
||||
"""Devuelve la lista de cabeceras canónicas para el template_id, o None si no existe."""
|
||||
return _TEMPLATE_HEADERS.get(template_id)
|
||||
headers = _TEMPLATE_HEADERS.get(template_id)
|
||||
if headers is None:
|
||||
return None
|
||||
return _apply_required_prefix(template_id, headers)
|
||||
|
||||
|
||||
def get_template_filename(template_id: str) -> str:
|
||||
|
||||
@@ -248,7 +248,8 @@ export const catalogosConfig: CsvUploadItem[] = [
|
||||
icon: Briefcase,
|
||||
modelTarget: 'Bom',
|
||||
templateId: 'boms',
|
||||
layoutModule: 'layouts_csv/boms'
|
||||
layoutModule: 'layouts_csv/boms',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'items',
|
||||
|
||||
Reference in New Issue
Block a user