"""Servicios para el manejo de pedimentos completos.""" import logging from typing import Any, Dict, List, Optional from fastapi import HTTPException # Importar controladores (nota: el archivo se llama controllers,py con coma) import sys import os sys.path.append(os.path.dirname(__file__)) from .controllers import pedimento_rest_controller, pedimento_vu_controller, pedimento_xml_scraper from utils.helpers import soap_error # Logger configurado para el módulo logger = logging.getLogger("app.api") async def consume_ws_get_pedimento_completo(**kwargs) -> Dict[str, Any]: """ Consume el servicio web para obtener pedimento completo. Args: **kwargs: Debe contener 'credencial' y 'pedimento' con sus respectivos campos Returns: Dict con 'documento' y 'xml_content' Raises: HTTPException: Si hay errores en la petición o datos faltantes """ # Validar datos de entrada credencial = kwargs.get('credencial', {}) pedimento_data = kwargs.get('pedimento', {}) if not credencial.get('user') or not credencial.get('password'): raise HTTPException(status_code=400, detail="Credenciales incompletas") required_fields = ['aduana', 'patente', 'pedimento', 'id', 'organizacion'] missing_fields = [f for f in required_fields if not pedimento_data.get(f)] if missing_fields: raise HTTPException( status_code=400, detail=f"Datos de pedimento incompletos: {missing_fields}" ) logger.info(f"Iniciando consulta SOAP para pedimento: {pedimento_data.get('pedimento')}") try: # Generar XML SOAP soap_xml = pedimento_vu_controller.generate_pedimento_completo_template( username=credencial.get('user'), password=credencial.get('password'), aduana=pedimento_data.get('aduana'), patente=pedimento_data.get('patente'), pedimento=pedimento_data.get('pedimento') ) soap_headers = { 'Content-Type': 'text/xml; charset=utf-8' } # Realizar petición SOAP soap_response = await pedimento_vu_controller.make_request_async( "ventanilla-ws-pedimentos/ConsultarPedimentoCompletoService?wsdl", data=soap_xml, headers=soap_headers ) if not soap_response: raise HTTPException(status_code=500, detail="No se recibió respuesta del servicio SOAP") if soap_error(soap_response): logger.error(f"Error en respuesta SOAP: {soap_response.text if hasattr(soap_response, 'text') else 'Sin detalles'}") raise HTTPException(status_code=500, detail="Error en la respuesta del servicio SOAP") # Extraer datos del XML try: data = pedimento_xml_scraper.extract_data(soap_response.text) except Exception as e: logger.error(f"Error al extraer datos XML: {e}") raise HTTPException(status_code=500, detail="Error al procesar respuesta XML") # Generar nombre de archivo file_name = f"vu_PC_{pedimento_data.get('pedimento_app', 'unknown')}.xml" # Enviar documento try: document_response = await pedimento_rest_controller.post_document( soap_response=soap_response, organizacion=pedimento_data.get('organizacion'), pedimento=pedimento_data.get('id'), file_name=file_name, document_type=2, ) except Exception as e: logger.error(f"Error al enviar documento: {e}") raise HTTPException(status_code=500, detail="Error al guardar documento") # Enriquecer datos con información del pedimento data['organizacion'] = pedimento_data.get('organizacion') data['id'] = pedimento_data.get('id') logger.info(f"Pedimento completo procesado exitosamente: {pedimento_data.get('pedimento')}") return { "documento": document_response, "xml_content": data } except HTTPException: raise except Exception as e: logger.error(f"Error inesperado en consume_ws_get_pedimento_completo: {e}") raise HTTPException(status_code=500, detail=f"Error interno: {str(e)}") async def put_pedimento_data(**kwargs) -> Dict[str, Any]: """ Actualiza la información del pedimento en el sistema REST. Args: **kwargs: Datos de credencial y pedimento Returns: Dict con resultados del procesamiento Raises: HTTPException: Si hay errores críticos en el procesamiento """ # Inicializar variables de respuesta result = { "documento": None, "pedimento_actualizado": None, "coves_procesados": None, "coves_error": None, "edocuments_procesados": None, "edocuments_error": None, "xml_content": None } # Obtener datos del servicio web try: ws_data = await consume_ws_get_pedimento_completo(**kwargs) result["documento"] = ws_data.get("documento", None) xml_content = ws_data.get('xml_content', {}) result["xml_content"] = xml_content if not xml_content: logger.warning("No se obtuvo contenido XML del servicio web") return result except HTTPException: raise # Re-lanzar HTTPExceptions except Exception as e: logger.error(f"Error inesperado al consumir servicio web: {e}") raise HTTPException(status_code=500, detail=f"Error al obtener datos del pedimento: {str(e)}") # Actualizar información del pedimento (crítico) try: result["pedimento_actualizado"] = await _update_pedimento_info(kwargs, xml_content) except Exception as e: logger.error(f"Error crítico al actualizar pedimento: {e}") raise HTTPException(status_code=500, detail=f"Error al actualizar el pedimento: {str(e)}") # Procesar COVEs (no crítico) try: result["coves_procesados"] = await _process_coves_safely(kwargs, xml_content) except Exception as e: logger.warning(f"Error al procesar COVEs: {e}") result["coves_error"] = str(e) # Procesar documentos digitalizados (no crítico) try: result["edocuments_procesados"] = await _process_edocuments_safely(kwargs, xml_content) except Exception as e: logger.warning(f"Error al procesar documentos digitalizados: {e}") result["edocuments_error"] = str(e) logger.info("Procesamiento de pedimento completo finalizado") return result async def _update_pedimento_info(kwargs: Dict[str, Any], xml_content: Dict[str, Any]) -> Optional[Dict[str, Any]]: """ Actualiza la información del pedimento. Args: kwargs: Datos originales xml_content: Contenido XML extraído Returns: Respuesta del servicio de actualización """ if not xml_content: logger.info("No hay contenido XML para actualizar el pedimento") return None # Preparar datos para actualización (excluir identificadores_ed) update_content = {k: v for k, v in xml_content.items() if k != 'identificadores_ed'} update_content['existe_expediente'] = True pedimento_id = kwargs.get('pedimento', {}).get('id') if not pedimento_id: raise ValueError("ID de pedimento no encontrado para actualización") response = await pedimento_rest_controller.put_pedimento(pedimento_id, update_content) logger.info(f"Pedimento {pedimento_id} actualizado exitosamente") return response async def _process_coves_safely(kwargs: Dict[str, Any], xml_content: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]: """ Procesa los COVEs de manera segura. """ coves = xml_content.get('coves', []) if not coves: logger.info("No se encontraron COVEs para procesar") return None logger.info(f"Procesando {len(coves)} COVEs encontrados") result = await _post_coves(kwargs.get('pedimento', {}), coves) logger.info(f"Se procesaron exitosamente {len(result)} COVEs") return result async def _process_edocuments_safely(kwargs: Dict[str, Any], xml_content: Dict[str, Any]) -> Optional[List[Dict[str, Any]]]: """ Procesa los documentos digitalizados de manera segura. """ identificadores_ed = xml_content.get('identificadores_ed', []) if not identificadores_ed: logger.info("No se encontraron documentos digitalizados (identificadores ED)") return None logger.info(f"Procesando {len(identificadores_ed)} documentos digitalizados...") result = await _post_edocuments(kwargs.get('pedimento', {}), identificadores_ed) logger.info(f"Se procesaron exitosamente {len(result)} documentos digitalizados") return result async def _post_coves(pedimento_data: Dict[str, Any], coves: List[str]) -> List[Dict[str, Any]]: """ Envía COVEs al sistema REST. Args: pedimento_data: Datos del pedimento coves: Lista de números de COVE Returns: Lista de respuestas exitosas Raises: HTTPException: Si no se pudo procesar ningún COVE """ if not coves: return [] responses = [] errors = [] for cove in coves: document_data = { 'numero_cove': cove, 'organizacion': pedimento_data.get('organizacion'), 'pedimento': pedimento_data.get('id') } try: response = await pedimento_rest_controller.post_cove(document_data) if response: responses.append(response) logger.debug(f"COVE {cove} procesado exitosamente") except Exception as e: error_msg = f"Error al procesar COVE {cove}: {str(e)}" logger.warning(error_msg) errors.append(error_msg) if not responses and coves: error_detail = f"No se pudo procesar ningún COVE. Errores: {'; '.join(errors)}" logger.error(error_detail) raise HTTPException(status_code=500, detail=error_detail) if errors: logger.warning(f"Se procesaron {len(responses)}/{len(coves)} COVEs. Errores: {len(errors)}") return responses async def _post_edocuments(pedimento_data: Dict[str, Any], identificadores_ed: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """ Envía documentos digitalizados al sistema REST. Args: pedimento_data: Datos del pedimento identificadores_ed: Lista de identificadores de documentos Returns: Lista de respuestas exitosas Raises: HTTPException: Si no se pudo procesar ningún documento """ if not identificadores_ed: return [] responses = [] errors = [] for identificador in identificadores_ed: try: # Validar campos requeridos if not identificador.get('clave') or not identificador.get('complemento1'): logger.warning(f"Documento con datos incompletos omitido: {identificador}") continue document_data = { 'clave': identificador.get('clave'), 'descripcion': identificador.get('descripcion', ''), 'numero_edocument': identificador.get('complemento1'), 'organizacion': pedimento_data.get('organizacion'), 'pedimento': pedimento_data.get('id') } response = await pedimento_rest_controller.post_edocument(document_data) if response: responses.append(response) logger.debug(f"Documento {identificador.get('clave')} procesado exitosamente") except Exception as e: error_msg = f"Error al procesar documento {identificador.get('clave', 'unknown')}: {str(e)}" logger.warning(error_msg) errors.append(error_msg) if not responses and identificadores_ed: error_detail = f"No se pudo procesar ningún documento digitalizado. Errores: {'; '.join(errors)}" logger.error(error_detail) raise HTTPException(status_code=500, detail=error_detail) if errors: logger.warning(f"Se procesaron {len(responses)}/{len(identificadores_ed)} documentos. Errores: {len(errors)}") return responses