from typing import Dict, Any, Optional class Generador551SCAII: @staticmethod def preparar_json(par: Dict[str, Any], id_factura: int, datos_parte: Dict[str, Any], um_data: Optional[Dict[str, Any]], met_val: str, cla_mon: str, num_remesa: int, num_partida_remesa: int, tipo_moneda_fac: str) -> Dict[str, Any]: """ Lógica para Registro 551 (Partidas) - SCAII. Implementa paridad total con la lógica de Clarion/WinDev. """ # 1. Lógica de Fracción (Regla Octava) frac_raw = par.get("Fraccion", "") frac_oct = par.get("FraccionOctava", "") frac_final = frac_oct if frac_oct else frac_raw # 2. Descripciones (Limpieza de CR/LF como en WinDev) desc_esp = par.get("Descripcion", " ").replace("\r", "").replace("\n", "").replace("\t", "") desc_ing = " " # Por ahora # 3. Arancel (Mapping de Clarion) tipo_frac = par.get("TipoFracc", "") arancel_map = {"TLCS": 3, "GENERAL": 0, "PROSEC": 1} id_arancel = str(arancel_map.get(tipo_frac, 0)) # 4. Lógica de Valor Comercial (Loc_ValorExpoMC) # ME = ValorDolares, MN = ValorPesos, else ValorComercial if tipo_moneda_fac == "ME": valor_comercial = float(par.get("ValorDolares") or 0) elif tipo_moneda_fac == "MN": valor_comercial = float(par.get("ValorPesos") or 0) else: valor_comercial = float(par.get("ValorComercial") or 0) # 5. Unidad de Medida OMA y Comercial (Mapeo reverso para WinDev) oma_final = str(par.get("ClaveOMA") or " ").strip() oma_raw = str(par.get("UnidadMedida", "")).strip().upper() if not oma_final or oma_final == "None": oma_final = "C62_1" if oma_raw in ["6", "PZA"] else oma_raw map_um_num = { "PZA": "6", "PIEZA": "6", "PZ": "6", "PC": "6", "PIEZAS": "6", "KGS": "1", "KGM": "1", "KG": "1", "KILOS": "1", "KILO": "1", "LTS": "5", "L": "5", "LITROS": "5", "LITRO": "5", "MTR": "2", "M": "2", "METROS": "2", "METRO": "2", "PAR": "7", "PARES": "7", "CJ": "3", "CAJA": "3", "CAJAS": "3", "JGO": "12", "JUEGO": "12", "SET": "12", "KIT": "12" } # Priorizar la clave que venga de la base de datos (CLAVEOMA) if um_data and um_data.get("CLAVEOMA"): um_comercial_num = str(um_data["CLAVEOMA"]).strip() else: um_comercial_num = map_um_num.get(oma_raw, oma_raw) # 6. Cálculo del Valor Agregado valor_agregado_me = float(par.get("ValorAgregadoME") or 0) valor_aduanas_me = float(par.get("ValorAduanasME") or 0) valor_dolares = float(par.get("ValorDolares") or 0) valor_aduanas_mn = float(par.get("ValorAduanasMN") or 0) valor_pesos = float(par.get("ValorPesos") or 0) costo_auxiliar_me = float(par.get("CostoAuxiliarME") or 0) cantidad = float(par.get("Cantidad") or 0) # 1ra Opción (EXPO): Si es Exportación, el valor ya viene calculado en ValorAgregadoME if valor_agregado_me > 0: valor_agregado = valor_agregado_me # 2da Opción (IMPO - Exactitud WinDev): Costo Unitario Auxiliar * Cantidad elif costo_auxiliar_me > 0: valor_agregado = costo_auxiliar_me * cantidad # 3ra Opción: Si Valor Aduanas ME existe elif valor_aduanas_me > 0 and valor_aduanas_me > valor_dolares: valor_agregado = valor_aduanas_me - valor_dolares # 4ta Opción: Calcular mediante el tipo de cambio implícito elif valor_aduanas_mn > 0 and valor_aduanas_mn > valor_pesos and valor_dolares > 0: tc_implicito = valor_pesos / valor_dolares if valor_dolares else 1 valor_agregado = (valor_aduanas_mn - valor_pesos) / tc_implicito else: valor_agregado = 0 valor_agregado = round(max(0, valor_agregado), 2) # 7. Método de Valoración específico por partida metodo_val = str(par.get("MetodoValoracion") or "").strip() if not metodo_val: metodo_val = str(met_val) if met_val else "" registro = { "id_factura": id_factura, "fraccion_551": str(frac_final), "descripcion_mercancia_551": desc_esp[:250], "numero_parte_551": str(par.get("NumParte") or " "), "valor_mercancia_551": valor_comercial, "cantidad_comercial_551": float(par.get("Cantidad") or 0), "unidad_medida_comercial_551": um_comercial_num, # Ej. "6" "cantidad_tarifa_551": float(par.get("Cantidad") or 0), # Se ajusta en el motor "umt_551": " ", # WinDev envía un espacio vacío aquí "valor_agregado_551": str(valor_agregado), "vinculacion_551": "0", "metodo_valoracion_551": metodo_val, "marca_551": str(par.get("Marca") or " ").strip() or " ", "modelo_551": str(par.get("Modelo") or " ").strip() or " ", "pais_origen_destino_551": str(par.get("PaisOrigen") or " "), "pais_comprador_vendedor_551": "MEX", # Generalmente MEX en Impo "entidad_federativa_origen_551": " ", "entidad_federativa_destino_551": " ", "entidad_federativa_comprador_551": " ", "entidad_federativa_vendedor_551": " ", "identificador_arancel_aplicar_551": id_arancel, "clave_industria_551": str(par.get("Sector") or " "), "peso_bruto_551": float(par.get("PesoBruto") or 0), "uso_mercancia_551": " ", "estado_mercancia_551": " ", "moneda_partida_551": str(cla_mon), "numero_factura_551": str(par.get("NumFactura") or " "), "tipo_bulto_551": " ", # Se llena en motor si aplica "cantidad_bultos_551": float(par.get("CantBultos") or 0), # --- PARIDAD WINDEV --- "peso_bruto_kg_551": float(par.get("PesoNeto") or 0), # WinDev envía Peso Neto aquí "peso_neto_kg_551": float(par.get("ValorAduanasMN") or 0), # HACK WinDev: Envía ValorAduanasMN aquí "numero_remesa_551": str(num_remesa or " "), "numero_partida_remesa_551": str(par.get("Linea") or num_partida_remesa), "cantidad_alterna_551": float(par.get("CantidadAlterna") or 0), "unidad_medida_alterna_551": str(par.get("UnidadAlterna") or " "), "descripcion_ingles_551": str(par.get("DescripcionIngles") or " ").strip()[:80], "identificador_sistema_scaii_scaf_551": "SCAII", "vu_submodelo_551": " ", "vu_serie_551": " ", "valor_unitario_aduana_551": float(par.get("CostoUnitarioME") or 0), "valor_dolares_551": float(par.get("ValorDolares") or 0), "unidad_medida_oma_551": oma_final, # Código de la fracción (ej. '6') "valor_dolares_cove_551": "0", "subdivision_fraccion_551": str(frac_final)[-2:] if len(str(frac_final)) >= 2 else "00", "codigo_carta_porte_551": " ", "nodo_551": "85858585", "CantidadComercialOMA_551": str(float(par.get("Cantidad") or 0)) } return registro