from http.client import HTTPException import base64 import re from .controllers import acuse_vu_controller, acuse_rest_controller from utils.helpers import soap_error import xml.etree.ElementTree as ET soap_headers = { 'Content-Type': 'text/xml; charset=utf-8', 'SOAPAction': 'http://www.ventanillaunica.gob.mx/ventanilla/ConsultaAcusesService/consultarAcuseEdocument',# AcuseCove 'Accept-Encoding': 'gzip,deflate', } async def obtener_acuse(**kwargs): soap_xml = acuse_vu_controller.generate_acuse_template(**kwargs) response = await acuse_vu_controller.make_request_async( "ventanilla-acuses-HA/ConsultaAcusesServiceWS?wsdl", data=soap_xml, headers=soap_headers ) if response is None: raise Exception("No se obtuvo respuesta del servicio SOAP.") if response.status_code != 200: raise Exception(f"Error en la solicitud SOAP: {response.status}") if (response) and (not soap_error(response)): acuse_base64 = _extract_acuse_data(response.text) if acuse_base64 is None: raise Exception("No se pudo extraer el acuse del documento de la respuesta SOAP.") pdf_bytes = _decode_acuse_base64_content(acuse_base64) if not pdf_bytes: raise HTTPException(status_code=500, detail="No se pudo decodificar el documento del acuse") # Validar que el PDF sea válido if not pdf_bytes.startswith(b'%PDF'): import logging logger = logging.getLogger("app.api") logger.warning("El contenido decodificado no parece ser un PDF válido") # Mejorar el nombre del archivo usando todos los datos relevantes pedimento = kwargs.get('pedimento', {}) pedimento_num = pedimento.get('pedimento','') _file_name = _get_file_name(**kwargs) # Validar que organización y pedimento no sean None organizacion = pedimento.get("organizacion", None) pedimento_id = pedimento.get("id", None) rest_response = await acuse_rest_controller.post_document( binary_content=pdf_bytes, organizacion=organizacion, pedimento=pedimento_id, file_name=_file_name, document_type=4 ) if rest_response is None: raise Exception("No se pudo enviar el acuse a la API interna.") if rest_response.get("id") is None: raise Exception("La respuesta de la API interna no contiene un ID válido.") acuse_update_response = await change_edocument_status( edoc=kwargs.get('edoc'), status=True, pedimento=pedimento ) return { "document_response": rest_response, "file_name": _file_name, "pedimento": pedimento_num, "acuse_update_response": acuse_update_response } async def change_edocument_status(edoc: dict, status: bool, pedimento: dict): data = { "id": edoc.get("id"), "edocument_descargado": status, "numero_edocument": edoc.get("numero_edocument"), "pedimento": pedimento.get("id"), "organizacion": pedimento.get("organizacion"), } response = await acuse_rest_controller.put_edocument(edocument_id=edoc.get("id"), data=data) return response def _decode_acuse_base64_content(base64_content): # Testeado """ Decodifica el contenido Base64 del acuse y limpia caracteres especiales. Args: base64_content (str): Contenido codificado en Base64 Returns: bytes: Contenido decodificado o None si hay error """ try: # Limpiar el contenido Base64 de manera exhaustiva cleaned_content = base64_content # Remover entidades HTML/XML como , , etc. cleaned_content = re.sub(r'&#x[0-9a-fA-F]+;', '', cleaned_content) cleaned_content = re.sub(r'&#[0-9]+;', '', cleaned_content) # Remover espacios en blanco, saltos de línea, etc. cleaned_content = re.sub(r'[\s\n\r\t]', '', cleaned_content) # Remover caracteres no válidos para Base64 cleaned_content = re.sub(r'[^A-Za-z0-9+/=]', '', cleaned_content) # Agregar padding si es necesario missing_padding = len(cleaned_content) % 4 if missing_padding: cleaned_content += '=' * (4 - missing_padding) # Decodificar Base64 decoded_content = base64.b64decode(cleaned_content) return decoded_content except Exception as e: # Intentar con validación estricta deshabilitada try: decoded_content = base64.b64decode(cleaned_content, validate=False) return decoded_content except Exception as e2: return None def _extract_acuse_data(soap_response_text: str) -> dict: try: # Primero, extraer la parte XML del contenido multipart xml_start = soap_response_text.find(' dict: pedimento = kwargs.get('pedimento', {}) pedimento_app = pedimento.get('pedimento_app', 'N/A') idEdocument = kwargs['edoc'].get('numero_edocument', 'N/A') _file_name = f"vu_AC_{pedimento_app}_{idEdocument}.pdf" return _file_name