189 lines
6.5 KiB
Python
189 lines
6.5 KiB
Python
from http.client import HTTPException
|
|
import base64
|
|
import re
|
|
import logging
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from utils.helpers import soap_error
|
|
from .controllers import edocs_rest_controller, edocs_vu_controller
|
|
|
|
logger = logging.getLogger("app.api")
|
|
|
|
soap_headers = {
|
|
'Content-Type': 'text/xml; charset=utf-8',
|
|
'SOAPAction': 'http://www.ventanillaunica.gob.mx/ventanilla/EdocumentosService/consultarEdocumento',
|
|
'Accept-Encoding': 'gzip,deflate',
|
|
}
|
|
|
|
# --- FUNCIONES AUXILIARES ---
|
|
|
|
def _decode_base64_content(base64_content):
|
|
try:
|
|
cleaned_content = re.sub(r'&#x[0-9a-fA-F]+;', '', base64_content)
|
|
cleaned_content = re.sub(r'&#[0-9]+;', '', cleaned_content)
|
|
cleaned_content = re.sub(r'[\s\n\r\t]', '', cleaned_content)
|
|
cleaned_content = re.sub(r'[^A-Za-z0-9+/=]', '', cleaned_content)
|
|
|
|
missing_padding = len(cleaned_content) % 4
|
|
if missing_padding:
|
|
cleaned_content += '=' * (4 - missing_padding)
|
|
|
|
return base64.b64decode(cleaned_content)
|
|
except Exception as e:
|
|
logger.error(f"Error al decodificar Base64: {e}")
|
|
try:
|
|
return base64.b64decode(cleaned_content, validate=False)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _extract_edoc_data(soap_response_text: str) -> str:
|
|
try:
|
|
xml_start = soap_response_text.find('<?xml')
|
|
if xml_start == -1:
|
|
return None
|
|
|
|
xml_content = soap_response_text[xml_start:]
|
|
boundary_end = xml_content.find('--uuid:')
|
|
if boundary_end != -1:
|
|
xml_content = xml_content[:boundary_end]
|
|
|
|
root = ET.fromstring(xml_content.strip())
|
|
|
|
namespaces = {
|
|
'S': 'http://schemas.xmlsoap.org/soap/envelope/',
|
|
'ns3': 'http://www.ventanillaunica.gob.mx/ws/consulta/edocs/'
|
|
}
|
|
|
|
edoc_elemento = root.find('.//ns3:responseConsultaEdocumento/documentoBase64', namespaces)
|
|
if edoc_elemento is None:
|
|
edoc_elemento = root.find('.//documentoBase64')
|
|
|
|
return edoc_elemento.text.strip() if edoc_elemento is not None and edoc_elemento.text else None
|
|
except ET.ParseError as e:
|
|
logger.error(f"Error parseando la respuesta SOAP para Edoc: {e}")
|
|
return None
|
|
except Exception as e:
|
|
logger.error(f"Error general en extracción de datos Edoc: {e}")
|
|
return None
|
|
|
|
|
|
def _get_file_name(**kwargs) -> str:
|
|
pedimento = kwargs.get('pedimento', {})
|
|
pedimento_app = pedimento.get('pedimento_app', 'N/A')
|
|
idEDocument = kwargs['edoc'].get('numero_edocument', 'N/A')
|
|
return f"vu_ED_{pedimento_app}_{idEDocument}.pdf"
|
|
|
|
# --- FUNCIONES DE SERVICIO ---
|
|
|
|
async def obtener_edoc(**kwargs):
|
|
soap_xml = edocs_vu_controller.generate_edoc_template(**kwargs)
|
|
|
|
response = await edocs_vu_controller.make_request_async(
|
|
"ventanilla-edocs-HA/EdocumentosServiceWS?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_code}")
|
|
|
|
if soap_error(response):
|
|
raise Exception("Respuesta SOAP contiene error de VUCEM.")
|
|
|
|
edoc_base64 = _extract_edoc_data(response.text)
|
|
if edoc_base64 is None:
|
|
raise Exception("No se pudo extraer el documento de la respuesta SOAP.")
|
|
|
|
pdf_bytes = _decode_base64_content(edoc_base64)
|
|
if not pdf_bytes:
|
|
raise HTTPException(status_code=500, detail="No se pudo decodificar el documento")
|
|
|
|
if not pdf_bytes.startswith(b'%PDF'):
|
|
logger.warning("El contenido decodificado no parece ser un PDF válido")
|
|
|
|
pedimento = kwargs.get('pedimento', {})
|
|
numero_documento = kwargs['edoc'].get('numero_edocument', '')
|
|
_file_name = _get_file_name(**kwargs)
|
|
|
|
organizacion = pedimento.get("organizacion", None)
|
|
pedimento_id = pedimento.get("id", None)
|
|
|
|
try:
|
|
with open(_file_name, "wb") as f:
|
|
f.write(pdf_bytes)
|
|
logger.info(f"PDF guardado localmente en {_file_name}")
|
|
except Exception as e:
|
|
logger.error(f"Error guardando el PDF localmente: {e}")
|
|
|
|
rest_response = await edocs_rest_controller.post_document(
|
|
binary_content=pdf_bytes,
|
|
organizacion=organizacion,
|
|
pedimento=pedimento_id,
|
|
file_name=_file_name,
|
|
document_type=5
|
|
)
|
|
|
|
if rest_response is None:
|
|
raise Exception("No se pudo enviar el documento 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.")
|
|
|
|
logger.info("Documento enviado, actualizando status de Edoc")
|
|
|
|
edoc_status_response = await change_edocument_status(
|
|
edoc=kwargs.get('edoc'),
|
|
status=True,
|
|
pedimento=pedimento
|
|
)
|
|
|
|
return {
|
|
"document_response": rest_response,
|
|
"file_name": _file_name,
|
|
"numero_documento": numero_documento,
|
|
"edoc_update_response": edoc_status_response if edoc_status_response else None
|
|
}
|
|
|
|
|
|
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 edocs_rest_controller.put_edocument(edocument_id=edoc.get("id"), data=data)
|
|
|
|
return response
|
|
|
|
async def obtener_edocs_masivo(**kwargs):
|
|
logger.info("Iniciando la orquestación de descarga masiva de Edocs.")
|
|
numeros_documentos = kwargs.get("edocs", [])
|
|
if not numeros_documentos:
|
|
return {"status": "warning", "message": "No se encontraron números de documento para procesar."}
|
|
|
|
for edoc in numeros_documentos:
|
|
try:
|
|
logger.info(f"Procesando Edoc: {edoc.get('numero_edocument', 'N/A')}")
|
|
edoc = {
|
|
"edoc": edoc,
|
|
"pedimento": kwargs.get("pedimento"),
|
|
"credencial": kwargs.get("credencial")
|
|
}
|
|
await obtener_edoc(**edoc)
|
|
logger.info(f"Edoc {edoc.get('numero_edocument', 'N/A')} procesado exitosamente.")
|
|
except Exception as e:
|
|
logger.error(f"Error procesando Edoc {edoc.get('numero_edocument', 'N/A')}: {str(e)}", exc_info=True)
|
|
continue # Continuar con el siguiente edoc en caso de error
|
|
|
|
return {
|
|
"status": "pending",
|
|
"total_documentos": len(numeros_documentos),
|
|
"message": "La orquestación de descarga masiva ha sido registrada."
|
|
}
|