se estan creando el registro de las tareas sin problemas

This commit is contained in:
2025-10-08 21:15:03 -06:00
parent 48db0d72d8
commit 770e0a4d13
15 changed files with 858 additions and 258 deletions

View File

@@ -1,68 +1,22 @@
from http.client import HTTPException
import base64
import re
import logging
import os
from typing import Any, Dict, Optional
import xml.etree.ElementTree as ET
from fastapi import HTTPException
from utils.helpers import soap_error
from .controllers import edocs_rest_controller, edocs_vu_controller
from ..common import create_service_response, create_error_response
# Logger para el módulo
logger = logging.getLogger("app.api")
# --- 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', {})
@@ -89,25 +43,102 @@ async def obtener_edoc(**kwargs):
data=soap_xml,
headers=soap_headers
)
# Validar respuesta del servicio SOAP
if response is None:
raise Exception("No se obtuvo respuesta del servicio SOAP.")
logger.error("No se obtuvo respuesta del servicio SOAP")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al contactar el servicio SOAP",
errors=["No se obtuvo respuesta del servicio"],
metadata={
"edoc_number": numero_documento,
"username": usuario
}
)
)
if response.status_code != 200:
raise Exception(f"Error en la solicitud SOAP: {response.status_code}")
logger.error(f"Error en la solicitud SOAP: {response.status_code}")
raise HTTPException(
status_code=response.status_code,
detail=create_error_response(
message="Error en la solicitud SOAP",
errors=[f"Código de estado: {response.status_code}"],
data={"soap_response": response.text[:500]},
metadata={
"status_code": response.status_code,
"edoc_number": numero_documento
}
)
)
if soap_error(response):
raise Exception("Respuesta SOAP contiene error de VUCEM.")
logger.error("Respuesta SOAP contiene error de VUCEM")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error en la respuesta del servicio SOAP",
errors=["La respuesta contiene un error de VUCEM"],
data={"soap_response": response.text[:500]},
metadata={"edoc_number": numero_documento}
)
)
try:
edoc_base64 = extract_pdf_bytes_from_xml_content(response.text)
except ValueError as ve:
logger.error(f"Error extrayendo contenido del XML: {ve}")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al procesar la respuesta SOAP",
errors=[str(ve)],
metadata={"edoc_number": numero_documento}
)
)
edoc_base64 = extract_pdf_bytes_from_xml_content(response.text)
if edoc_base64 is None:
raise Exception("No se pudo extraer el documento de la respuesta SOAP.")
logger.error("No se pudo extraer el documento de la respuesta SOAP")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al extraer el documento",
errors=["No se pudo encontrar el documento en la respuesta SOAP"],
metadata={"edoc_number": numero_documento}
)
)
pdf_bytes = edoc_base64['pdf_bytes']
if not pdf_bytes:
raise HTTPException(status_code=500, detail="No se pudo decodificar el documento")
logger.error("No se pudo decodificar el documento PDF")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al decodificar el documento",
errors=["El contenido del documento está vacío o es inválido"],
metadata={
"edoc_number": numero_documento,
"has_cadena_original": bool(edoc_base64.get('cadena_original')),
"has_sello_digital": bool(edoc_base64.get('sello_digital'))
}
)
)
# Validar formato PDF
if not pdf_bytes.startswith(b'%PDF'):
logger.warning("El contenido decodificado no parece ser un PDF válido")
logger.error("El contenido decodificado no es un PDF válido")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="El documento recibido no es un PDF válido",
errors=["El contenido no tiene el formato PDF esperado"],
metadata={
"edoc_number": numero_documento,
"content_start": str(pdf_bytes[:20])
}
)
)
pedimento = kwargs.get('pedimento', {})
numero_documento = kwargs['edoc'].get('numero_edocument', '')
@@ -116,12 +147,8 @@ async def obtener_edoc(**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}")
# No guardaremos el archivo localmente por seguridad
logger.debug(f"Procesando documento {numero_documento} para pedimento {pedimento_id}")
rest_response = await edocs_rest_controller.post_document(
binary_content=pdf_bytes,
@@ -132,26 +159,61 @@ async def obtener_edoc(**kwargs):
)
if rest_response is None:
raise Exception("No se pudo enviar el documento a la API interna.")
logger.error("Error al enviar el documento a la API interna")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al guardar el documento en el sistema",
errors=["No se pudo enviar el documento a la API interna"],
metadata={
"file_name": _file_name,
"edoc_number": numero_documento
}
)
)
if rest_response.get("id") is None:
raise Exception("La respuesta de la API interna no contiene un ID válido.")
logger.error("Respuesta de API interna sin ID válido")
raise HTTPException(
status_code=500,
detail=create_error_response(
message="Error al procesar la respuesta del sistema",
errors=["La respuesta de la API no contiene un ID válido"],
data={"api_response": rest_response}
)
)
logger.info("Documento enviado, actualizando status de Edoc")
edoc_status_response = await change_edocument_status(
edoc=doc,
status=True,
pedimento=pedimento
)
print(edoc_status_response)
try:
edoc_status_response = await change_edocument_status(
edoc=doc,
status=True,
pedimento=pedimento
)
except Exception as e:
logger.warning(f"Error al actualizar estado del documento: {e}")
# No fallamos aquí porque el documento ya se guardó exitosamente
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
}
logger.info(f"E-document {numero_documento} procesado exitosamente")
return create_service_response(
message=f"E-document {numero_documento} procesado exitosamente",
data={
"document_response": rest_response,
"file_name": _file_name,
"numero_documento": numero_documento,
"edoc_update_response": edoc_status_response if edoc_status_response else None
},
metadata={
"document_type": 5,
"pedimento_app": pedimento.get('pedimento_app'),
"organizacion": organizacion,
"content_type": "application/pdf",
"has_cadena_original": bool(edoc_base64.get('cadena_original')),
"has_sello_digital": bool(edoc_base64.get('sello_digital'))
}
)
async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
@@ -168,8 +230,6 @@ async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
return response
def extract_pdf_bytes_from_xml_content(xml_content: str):
"""
Extrae el PDF y metadatos desde un string XML.