se estan creando el registro de las tareas sin problemas
This commit is contained in:
@@ -2,9 +2,8 @@ from fastapi import APIRouter, HTTPException, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
|
||||
|
||||
from api.api_v2.modules.authentication.services import get_current_user
|
||||
from api.api_v2.modules.tasks.services import register_task
|
||||
from .schemas import AcuseSchema, AcuseMasivoSchema
|
||||
from .tasks import process_acuse_request
|
||||
|
||||
@@ -19,7 +18,17 @@ async def obtener_acuse(acuse_request: AcuseSchema):
|
||||
acuse_dict = acuse_request.model_dump()
|
||||
# Ejecuta la tarea de Celery de forma asíncrona
|
||||
task = process_acuse_request.delay(acuse_dict)
|
||||
# Puedes devolver el ID de la tarea para consultar el estado después
|
||||
|
||||
# Registra la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando acuse para pedimento {acuse_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=acuse_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=acuse_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=6 # 6 corresponde a "Acuse"
|
||||
)
|
||||
|
||||
return {"task_id": task.id, "status": "submitted"}
|
||||
|
||||
|
||||
@@ -40,4 +49,14 @@ async def obtener_acuses(acuse_request: AcuseMasivoSchema):
|
||||
task = process_acuse_request.delay(acuse_dict)
|
||||
task_ids.append(task.id)
|
||||
|
||||
# Registra cada tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando acuse masivo para pedimento {acuse_request_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=acuse_request_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=acuse_request_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=6 # 6 corresponde a "Acuse"
|
||||
)
|
||||
|
||||
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
from http.client import HTTPException
|
||||
import base64
|
||||
import re
|
||||
import logging
|
||||
from typing import Any, Dict, Optional
|
||||
import xml.etree.ElementTree as ET
|
||||
from fastapi import HTTPException
|
||||
|
||||
from .controllers import acuse_vu_controller, acuse_rest_controller
|
||||
from utils.helpers import soap_error
|
||||
from ..common import create_service_response, create_error_response
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
# Logger configurado para el módulo
|
||||
logger = logging.getLogger("app.api")
|
||||
|
||||
soap_headers = {
|
||||
'Content-Type': 'text/xml; charset=utf-8',
|
||||
@@ -23,29 +29,68 @@ async def obtener_acuse(**kwargs):
|
||||
)
|
||||
|
||||
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"]
|
||||
)
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Error en la solicitud SOAP: {response.status}")
|
||||
logger.error(f"Error en la solicitud SOAP: {response.status_code}")
|
||||
raise HTTPException(
|
||||
status_code=response.status_code,
|
||||
detail=create_error_response(
|
||||
message=f"Error en la solicitud SOAP: {response.status_code}",
|
||||
data={"soap_response": response.text[:500]}
|
||||
)
|
||||
)
|
||||
|
||||
if (response) and (not soap_error(response)):
|
||||
acuse_base64 = _extract_acuse_data(response.text)
|
||||
if soap_error(response):
|
||||
logger.error("Error SOAP detectado en la respuesta")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error en la respuesta del servicio SOAP",
|
||||
data={"soap_response": response.text[:500]}
|
||||
)
|
||||
)
|
||||
|
||||
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.")
|
||||
|
||||
logger.error("No se pudo extraer el acuse del documento")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="No se pudo extraer el acuse del documento",
|
||||
errors=["El formato de la respuesta SOAP no es el esperado"]
|
||||
)
|
||||
)
|
||||
|
||||
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")
|
||||
logger.error("No se pudo decodificar el contenido Base64 del acuse")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="No se pudo decodificar el documento del acuse",
|
||||
errors=["El contenido Base64 no es válido"]
|
||||
)
|
||||
)
|
||||
|
||||
# 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")
|
||||
|
||||
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={"content_start": str(pdf_bytes[:20])}
|
||||
)
|
||||
)
|
||||
|
||||
# Mejorar el nombre del archivo usando todos los datos relevantes
|
||||
pedimento = kwargs.get('pedimento', {})
|
||||
@@ -65,21 +110,49 @@ async def obtener_acuse(**kwargs):
|
||||
)
|
||||
|
||||
if rest_response is None:
|
||||
raise Exception("No se pudo enviar el acuse a la API interna.")
|
||||
logger.error("Error al enviar el acuse a la API interna")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error al guardar el acuse en el sistema",
|
||||
errors=["No se pudo enviar el documento a la API interna"],
|
||||
metadata={"file_name": _file_name}
|
||||
)
|
||||
)
|
||||
|
||||
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}
|
||||
)
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return create_service_response(
|
||||
message="Acuse procesado exitosamente",
|
||||
data={
|
||||
"document_response": rest_response,
|
||||
"file_name": _file_name,
|
||||
"pedimento": pedimento_num,
|
||||
"acuse_update_response": acuse_update_response
|
||||
},
|
||||
metadata={
|
||||
"document_type": 4,
|
||||
"pedimento_app": pedimento.get('pedimento_app'),
|
||||
"organizacion": organizacion,
|
||||
"edoc_number": kwargs.get('edoc', {}).get('numero_edocument'),
|
||||
"content_type": "application/pdf"
|
||||
}
|
||||
)
|
||||
|
||||
async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
|
||||
data = {
|
||||
|
||||
4
api/api_v2/modules/common/__init__.py
Normal file
4
api/api_v2/modules/common/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
"""Módulo común para funcionalidades compartidas entre servicios."""
|
||||
from .response import create_service_response, create_error_response
|
||||
|
||||
__all__ = ['create_service_response', 'create_error_response']
|
||||
67
api/api_v2/modules/common/response.py
Normal file
67
api/api_v2/modules/common/response.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Utilidades para estandarizar respuestas en los servicios."""
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
def create_service_response(
|
||||
success: bool = True,
|
||||
message: str = "Operación completada exitosamente",
|
||||
data: Optional[Dict[str, Any]] = None,
|
||||
errors: Optional[List[str]] = None,
|
||||
warnings: Optional[List[str]] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Crea una respuesta estandarizada para los servicios.
|
||||
|
||||
Args:
|
||||
success: Indica si la operación fue exitosa
|
||||
message: Mensaje principal de la respuesta
|
||||
data: Datos principales de la respuesta
|
||||
errors: Lista de errores si los hay
|
||||
warnings: Lista de advertencias si las hay
|
||||
metadata: Metadatos adicionales de la operación
|
||||
|
||||
Returns:
|
||||
Dict con estructura estandarizada de respuesta
|
||||
"""
|
||||
response = {
|
||||
"success": success,
|
||||
"message": message,
|
||||
"data": data or {},
|
||||
}
|
||||
|
||||
if errors:
|
||||
response["errors"] = errors
|
||||
|
||||
if warnings:
|
||||
response["warnings"] = warnings
|
||||
|
||||
if metadata:
|
||||
response["metadata"] = metadata
|
||||
|
||||
return response
|
||||
|
||||
def create_error_response(
|
||||
message: str,
|
||||
errors: Optional[List[str]] = None,
|
||||
data: Optional[Dict[str, Any]] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Crea una respuesta de error estandarizada.
|
||||
|
||||
Args:
|
||||
message: Mensaje principal de error
|
||||
errors: Lista detallada de errores
|
||||
data: Datos adicionales del error
|
||||
metadata: Metadatos del error
|
||||
|
||||
Returns:
|
||||
Dict con estructura estandarizada de error
|
||||
"""
|
||||
return create_service_response(
|
||||
success=False,
|
||||
message=message,
|
||||
data=data,
|
||||
errors=errors,
|
||||
metadata=metadata
|
||||
)
|
||||
@@ -1,58 +1,106 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from .schemas import CoveListSchema, CoveRequestSchema
|
||||
from typing import List
|
||||
from typing import List, Dict, Any
|
||||
from uuid import UUID
|
||||
|
||||
from .tasks import process_cove_request, process_acuse_cove_request
|
||||
from ..tasks.services import register_task
|
||||
|
||||
router = APIRouter()
|
||||
# Aquí puedes definir tus endpoints relacionados con COVES usando el esquema CoveBaseSchema
|
||||
|
||||
|
||||
|
||||
@router.post("/services/cove/", response_model=dict)
|
||||
@router.post("/services/cove/", response_model=Dict[str, Any])
|
||||
async def get_cove(cove: CoveRequestSchema):
|
||||
# Lógica para obtener un COVE
|
||||
task = process_cove_request.delay(cove.model_dump())
|
||||
"""Endpoint para obtener un COVE específico."""
|
||||
cove_dict = cove.model_dump()
|
||||
task = process_cove_request.delay(cove_dict)
|
||||
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando COVE para pedimento {cove_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=cove_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=cove_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=8 # 8 corresponde a "Cove"
|
||||
)
|
||||
|
||||
return {"task_id": task.id, "status": "submitted"}
|
||||
|
||||
|
||||
@router.post("/services/all/coves", response_model=dict)
|
||||
@router.post("/services/all/coves", response_model=Dict[str, Any])
|
||||
async def get_coves(coves_request: CoveListSchema):
|
||||
# Lógica para obtener un COVE
|
||||
"""Endpoint para obtener múltiples COVEs asociados a un pedimento."""
|
||||
task_ids = []
|
||||
coves_dict = coves_request.model_dump()
|
||||
pedimento = coves_dict.get('pedimento', {})
|
||||
|
||||
for cove in coves_dict.get('coves', []):
|
||||
cove_dict = {
|
||||
"cove": cove,
|
||||
"pedimento": coves_dict.get('pedimento'),
|
||||
"pedimento": pedimento,
|
||||
"credencial": coves_dict.get('credencial')
|
||||
}
|
||||
task = process_cove_request.delay(cove_dict)
|
||||
task_ids.append(task.id)
|
||||
|
||||
# Registrar cada tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando COVE masivo para pedimento {pedimento.get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=pedimento.get('id'),
|
||||
organizacion_id=pedimento.get('organizacion'),
|
||||
servicio=8 # 8 corresponde a "Cove"
|
||||
)
|
||||
|
||||
return {"task_id": task.id, "coves_tasks_ids": task_ids, "status": "submitted"}
|
||||
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||
|
||||
|
||||
@router.post("/services/acuse/cove/", response_model=dict)
|
||||
@router.post("/services/acuse/cove/", response_model=Dict[str, Any])
|
||||
async def get_acuse_cove(cove: CoveRequestSchema):
|
||||
# Lógica para obtener un COVE
|
||||
task = process_acuse_cove_request.delay(cove.model_dump())
|
||||
"""Endpoint para obtener el acuse de un COVE específico."""
|
||||
cove_dict = cove.model_dump()
|
||||
task = process_acuse_cove_request.delay(cove_dict)
|
||||
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando acuse de COVE para pedimento {cove_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=cove_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=cove_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=9 # 9 corresponde a "Acuse Cove"
|
||||
)
|
||||
|
||||
return {"task_id": task.id, "status": "submitted"}
|
||||
|
||||
@router.post("/services/all/acuse/cove/")
|
||||
@router.post("/services/all/acuse/cove/", response_model=Dict[str, Any])
|
||||
async def get_acuses_cove(coves_request: CoveListSchema):
|
||||
# Lógica para obtener un COVE
|
||||
"""Endpoint para obtener los acuses de múltiples COVEs asociados a un pedimento."""
|
||||
task_ids = []
|
||||
coves_dict = coves_request.model_dump()
|
||||
pedimento = coves_dict.get('pedimento', {})
|
||||
|
||||
for cove in coves_dict.get('coves', []):
|
||||
acuse_dict = {
|
||||
"cove": cove,
|
||||
"pedimento": coves_dict.get('pedimento'),
|
||||
"pedimento": pedimento,
|
||||
"credencial": coves_dict.get('credencial')
|
||||
}
|
||||
task = process_acuse_cove_request.delay(acuse_dict)
|
||||
task_ids.append(task.id)
|
||||
|
||||
# Registrar cada tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando acuse masivo de COVE para pedimento {pedimento.get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=pedimento.get('id'),
|
||||
organizacion_id=pedimento.get('organizacion'),
|
||||
servicio=9 # 9 corresponde a "Acuse Cove"
|
||||
)
|
||||
|
||||
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||
@@ -2,17 +2,20 @@ import base64
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import tempfile
|
||||
from typing import Any, Dict, List, Optional
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from fastapi import HTTPException
|
||||
from controllers.RESTController import rest_controller
|
||||
from controllers.SOAPController import soap_controller
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
||||
import tempfile
|
||||
|
||||
from utils.helpers import soap_error
|
||||
from .controllers import coves_vu_controller, coves_rest_controller
|
||||
from ..common import create_service_response, create_error_response
|
||||
|
||||
# Logger para el módulo
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -42,8 +45,26 @@ async def consume_ws_get_cove(**kwargs):
|
||||
cove = kwargs['cove'].get('cove', None)
|
||||
|
||||
if not credenciales or not username or not cove:
|
||||
raise Exception(
|
||||
"Credenciales o COVE no proporcionados correctamente")
|
||||
missing = []
|
||||
if not credenciales:
|
||||
missing.append("credenciales")
|
||||
if not username:
|
||||
missing.append("nombre de usuario")
|
||||
if not cove:
|
||||
missing.append("número de COVE")
|
||||
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=create_error_response(
|
||||
message="Datos incompletos para procesar COVE",
|
||||
errors=[f"Falta: {', '.join(missing)}"],
|
||||
metadata={"provided_data": {
|
||||
"has_credentials": bool(credenciales),
|
||||
"has_username": bool(username),
|
||||
"has_cove": bool(cove)
|
||||
}}
|
||||
)
|
||||
)
|
||||
|
||||
logger.info(f"Procesando COVE: {cove} para usuario: {username}")
|
||||
|
||||
@@ -120,18 +141,38 @@ async def consume_ws_get_cove(**kwargs):
|
||||
|
||||
logger.info(f"COVE {cove} procesado exitosamente")
|
||||
|
||||
# Asegurar que la respuesta sea serializable
|
||||
result = {
|
||||
"documento": document_response if document_response else None,
|
||||
"cove_update_response": cove_status_response if cove_status_response else None
|
||||
}
|
||||
|
||||
return result
|
||||
return create_service_response(
|
||||
message=f"COVE {cove} procesado exitosamente",
|
||||
data={
|
||||
"documento": document_response if document_response else None,
|
||||
"cove_update_response": cove_status_response if cove_status_response else None
|
||||
},
|
||||
metadata={
|
||||
"cove_number": cove,
|
||||
"file_name": _file_name,
|
||||
"document_type": 8,
|
||||
"pedimento_app": pedimento_app,
|
||||
"username": username,
|
||||
"organizacion": kwargs.get('pedimento', {}).get('organizacion')
|
||||
}
|
||||
)
|
||||
|
||||
except HTTPException as he:
|
||||
raise he
|
||||
except Exception as e:
|
||||
logger.error(f"Error procesando COVE: {str(e)}", exc_info=True)
|
||||
# Asegurar que no se retornen datos binarios en el error
|
||||
raise Exception(f"Error interno al procesar COVE: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error interno al procesar COVE",
|
||||
errors=[str(e)],
|
||||
metadata={
|
||||
"cove_number": cove,
|
||||
"username": username,
|
||||
"pedimento_app": pedimento_app
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def consume_ws_get_acuse_cove(**kwargs):
|
||||
@@ -155,20 +196,57 @@ async def consume_ws_get_acuse_cove(**kwargs):
|
||||
)
|
||||
|
||||
if response is None:
|
||||
raise Exception("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 SOAP"],
|
||||
metadata={
|
||||
"cove_number": kwargs['cove'].get('cove'),
|
||||
"pedimento_app": kwargs.get('pedimento', {}).get('pedimento_app')
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Error en la solicitud SOAP: {response.status}")
|
||||
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,
|
||||
"cove_number": kwargs['cove'].get('cove')
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if soap_error(response):
|
||||
rest_response = await coves_rest_controller.post_document(
|
||||
soap_response=response,
|
||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||
pedimento=kwargs.get('pedimento').get('id'),
|
||||
file_name=f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml",
|
||||
document_type=10,
|
||||
error_file_name = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml"
|
||||
try:
|
||||
rest_response = await coves_rest_controller.post_document(
|
||||
soap_response=response,
|
||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||
pedimento=kwargs.get('pedimento').get('id'),
|
||||
file_name=error_file_name,
|
||||
document_type=10,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error al guardar respuesta SOAP errónea: {e}")
|
||||
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error en la respuesta del servicio SOAP",
|
||||
errors=["Se detectó un error en la respuesta SOAP"],
|
||||
data={"error_file": error_file_name} if 'rest_response' in locals() else None,
|
||||
metadata={
|
||||
"cove_number": kwargs['cove'].get('cove'),
|
||||
"document_type": 10
|
||||
}
|
||||
)
|
||||
)
|
||||
raise Exception("Error detectado en la respuesta SOAP.")
|
||||
if (response) and (not soap_error(response)):
|
||||
logger.debug(f"Respuesta SOAP recibida, extrayendo acuse...")
|
||||
acuse_base64 = _extract_acuse_data(response.text)
|
||||
@@ -224,12 +302,22 @@ async def consume_ws_get_acuse_cove(**kwargs):
|
||||
pedimento=kwargs.get('pedimento')
|
||||
)
|
||||
|
||||
return {
|
||||
"document_response": rest_response,
|
||||
"file_name": _file_name,
|
||||
"pedimento": pedimento_num,
|
||||
"acuse_update": acuse_status
|
||||
}
|
||||
return create_service_response(
|
||||
message="Acuse de COVE procesado exitosamente",
|
||||
data={
|
||||
"document_response": rest_response,
|
||||
"file_name": _file_name,
|
||||
"pedimento": pedimento_num,
|
||||
"acuse_update": acuse_status
|
||||
},
|
||||
metadata={
|
||||
"document_type": 7,
|
||||
"pedimento_app": pedimento.get('pedimento_app'),
|
||||
"organizacion": organizacion,
|
||||
"cove_number": kwargs['cove'].get('cove'),
|
||||
"content_type": "application/pdf"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _decode_acuse_base64_content(base64_content): # Testeado
|
||||
@@ -462,15 +550,26 @@ async def fetch_sign_and_cer(cadena_original: str, username: str, credenciales:
|
||||
return firma, certificado, tmp_key_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error obteniendo certificado/llave o generando firma: {e}")
|
||||
logger.error(f"Error obteniendo certificado/llave o generando firma: {e}")
|
||||
# Limpiar archivo temporal si existe
|
||||
if 'tmp_key_path' in locals() and os.path.exists(tmp_key_path):
|
||||
try:
|
||||
os.remove(tmp_key_path)
|
||||
except:
|
||||
pass
|
||||
raise Exception(f"Error en fetch_sign_and_cer: {str(e)}")
|
||||
except Exception as cleanup_error:
|
||||
logger.warning(f"Error al limpiar archivo temporal: {cleanup_error}")
|
||||
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error al procesar certificado y firma",
|
||||
errors=[str(e)],
|
||||
metadata={
|
||||
"username": username,
|
||||
"has_key": bool(key_bytes) if 'key_bytes' in locals() else False,
|
||||
"has_cert": bool(cer) if 'cer' in locals() else False
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def sign_chain_original(key_path: str, password: str, cadena_original: str) -> str:
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import Dict, Any, Optional
|
||||
from .schemas import EdocumentsSchema, EdocumentsMasivoSchema
|
||||
from .tasks import process_edoc_download_request
|
||||
from api.api_v2.modules.authentication.services import get_current_user
|
||||
from api.api_v2.modules.tasks.services import register_task
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -17,6 +18,15 @@ async def download_edoc(edoc_request: EdocumentsSchema):
|
||||
edoc_dict = edoc_request.model_dump()
|
||||
# Ejecuta la tarea de Celery de forma asíncrona
|
||||
task = process_edoc_download_request.delay(edoc_dict)
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando descarga de E-Document {edoc_dict.get('edoc', {}).get('numero_edocument', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=edoc_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=edoc_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=7 # 7 corresponde a "EDocument"
|
||||
)
|
||||
# Devuelve el ID de la tarea
|
||||
return {"task_id": task.id, "status": "submitted"}
|
||||
|
||||
@@ -37,5 +47,14 @@ async def download_edocs_masivo(edoc_request: EdocumentsMasivoSchema):
|
||||
}
|
||||
task = process_edoc_download_request.delay(edoc_dict)
|
||||
task_ids.append(task.id)
|
||||
# Registrar cada tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando descarga masiva de E-Document {edoc.get('numero_edocument', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=edoc_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=edoc_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=7 # 7 corresponde a "EDocument"
|
||||
)
|
||||
|
||||
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||
@@ -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,27 +159,62 @@ 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
|
||||
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
|
||||
|
||||
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'))
|
||||
}
|
||||
)
|
||||
|
||||
print(edoc_status_response)
|
||||
|
||||
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 = {
|
||||
@@ -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.
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Dict, Any, List, Optional
|
||||
|
||||
|
||||
from api.api_v2.modules.authentication.services import get_current_user
|
||||
from api.api_v2.modules.tasks.services import register_task
|
||||
from .schemas import PartidaRequestSchema, PartidaListSchema
|
||||
from .tasks import process_partida_request
|
||||
|
||||
@@ -19,6 +20,15 @@ async def obtener_partida(partida_request: PartidaRequestSchema):
|
||||
acuse_dict = partida_request.model_dump()
|
||||
# Ejecuta la tarea de Celery de forma asíncrona
|
||||
task = process_partida_request.delay(acuse_dict)
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando partida {acuse_dict.get('partida', {}).get('numero', 'N/A')} para pedimento {acuse_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=acuse_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=acuse_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=4 # 4 corresponde a "Pedimento Partidas"
|
||||
)
|
||||
# Puedes devolver el ID de la tarea para consultar el estado después
|
||||
return {"task_id": task.id, "status": "submitted"}
|
||||
|
||||
@@ -39,5 +49,14 @@ async def obtener_partidas(partidas_request: PartidaListSchema):
|
||||
}
|
||||
task = process_partida_request.delay(partida_dict)
|
||||
task_ids.append(task.id)
|
||||
# Registrar cada tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando partida masiva {partida.get('numero', 'N/A')} para pedimento {partida_request_dict.get('pedimento', {}).get('pedimento_app', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=partida_request_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=partida_request_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=4 # 4 corresponde a "Pedimento Partidas"
|
||||
)
|
||||
|
||||
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||
@@ -1,18 +1,8 @@
|
||||
import base64
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from fastapi import HTTPException
|
||||
from controllers.RESTController import rest_controller
|
||||
from controllers.SOAPController import soap_controller
|
||||
from cryptography.hazmat.primitives import serialization, hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
||||
import tempfile
|
||||
|
||||
from utils.helpers import soap_error
|
||||
from .controllers import partida_rest_controller, partida_vu_controller
|
||||
from ..common import create_service_response, create_error_response
|
||||
|
||||
# Logger para el módulo
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -30,99 +20,168 @@ async def consume_ws_get_partida(**kwargs):
|
||||
Raises:
|
||||
Exception: Si hay errores en el procesamiento
|
||||
"""
|
||||
try:
|
||||
logger.info("Iniciando procesamiento de partidas")
|
||||
credenciales = kwargs.get('credencial')
|
||||
username = credenciales.get('user')
|
||||
pedimento_app = kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')
|
||||
partida = kwargs.get('partida', {})
|
||||
logger.info("Iniciando procesamiento de partidas")
|
||||
credenciales = kwargs.get('credencial')
|
||||
username = credenciales.get('user')
|
||||
pedimento_app = kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')
|
||||
partida = kwargs.get('partida', {})
|
||||
|
||||
if not credenciales or not username or not partida:
|
||||
raise Exception("Credenciales o Partida no proporcionados correctamente")
|
||||
|
||||
logger.info(f"Procesando Partida: {partida} para usuario: {username}")
|
||||
|
||||
# Generar template SOAP
|
||||
|
||||
soap_xml = partida_vu_controller.generate_partidas_template(
|
||||
username=username,
|
||||
password=credenciales.get('password'),
|
||||
aduana=kwargs.get('pedimento', {}).get('aduana', 'N/A'),
|
||||
patente=kwargs.get('pedimento', {}).get('patente', 'N/A'),
|
||||
pedimento=kwargs.get('pedimento', {}).get('pedimento', 'N/A'),
|
||||
numero_operacion=kwargs.get('pedimento', {}).get('numero_operacion', ''),
|
||||
partida=partida.get('numero', '')
|
||||
if not credenciales or not username or not partida:
|
||||
logger.error("Credenciales o Partida faltantes")
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=create_error_response(
|
||||
message="Datos incompletos para procesar la partida",
|
||||
errors=["Credenciales o datos de partida no proporcionados correctamente"],
|
||||
metadata={
|
||||
"has_credentials": bool(credenciales),
|
||||
"has_username": bool(username),
|
||||
"has_partida": bool(partida)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
soap_headers = {
|
||||
'Content-Type': 'text/xml; charset=utf-8'
|
||||
}
|
||||
logger.info(f"Procesando Partida {partida.get('numero', 'N/A')} para usuario {username}")
|
||||
|
||||
logger.info("Enviando petición SOAP a VUCEM")
|
||||
soap_response = await partida_vu_controller.make_request_async(
|
||||
"/ventanilla-ws-pedimentos/ConsultarPartidaService",
|
||||
data=soap_xml,
|
||||
headers=soap_headers
|
||||
)
|
||||
# Generar template SOAP
|
||||
soap_xml = partida_vu_controller.generate_partidas_template(
|
||||
username=username,
|
||||
password=credenciales.get('password'),
|
||||
aduana=kwargs.get('pedimento', {}).get('aduana', 'N/A'),
|
||||
patente=kwargs.get('pedimento', {}).get('patente', 'N/A'),
|
||||
pedimento=kwargs.get('pedimento', {}).get('pedimento', 'N/A'),
|
||||
numero_operacion=kwargs.get('pedimento', {}).get('numero_operacion', ''),
|
||||
partida=partida.get('numero', '')
|
||||
)
|
||||
|
||||
soap_headers = {
|
||||
'Content-Type': 'text/xml; charset=utf-8'
|
||||
}
|
||||
|
||||
if not soap_response:
|
||||
raise Exception("No se recibió respuesta del servicio SOAP")
|
||||
logger.info("Enviando petición SOAP a VUCEM")
|
||||
soap_response = await partida_vu_controller.make_request_async(
|
||||
"/ventanilla-ws-pedimentos/ConsultarPartidaService",
|
||||
data=soap_xml,
|
||||
headers=soap_headers
|
||||
)
|
||||
|
||||
if soap_error(soap_response):
|
||||
if not soap_response:
|
||||
logger.error("No se recibió 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={
|
||||
"partida_numero": partida.get('numero'),
|
||||
"username": username
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if soap_error(soap_response):
|
||||
error_file_name = f"vu_PT_{pedimento_app}_{partida.get('numero', '')}_ERROR.xml"
|
||||
try:
|
||||
document_response = await partida_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||
pedimento=kwargs.get('pedimento').get('id'),
|
||||
file_name=f"vu_PT_{pedimento_app}_{partida.get('numero', '')}_ERROR.xml",
|
||||
file_name=error_file_name,
|
||||
document_type=10,
|
||||
)
|
||||
raise Exception("Error en la respuesta del servicio SOAP")
|
||||
|
||||
logger.info("Respuesta SOAP exitosa, enviando documento")
|
||||
|
||||
# Enviar documento
|
||||
_file_name = f"vu_PT_{pedimento_app}_{partida.get('numero', '')}.xml"
|
||||
try:
|
||||
|
||||
document_response = await partida_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||
pedimento=kwargs.get('pedimento').get('id'),
|
||||
file_name=_file_name,
|
||||
document_type=1,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error detectado en la respuesta SOAP: {str(e)}")
|
||||
raise Exception(f"Error en la respuesta SOAP: {str(e)}")
|
||||
logger.error(f"Error al guardar la respuesta de error: {e}")
|
||||
# Continuamos con el error original
|
||||
|
||||
logger.error("Error en la respuesta del servicio SOAP")
|
||||
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": soap_response.text[:500] if hasattr(soap_response, 'text') else None},
|
||||
metadata={
|
||||
"partida_numero": partida.get('numero'),
|
||||
"error_file": error_file_name
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("Documento enviado, actualizando status de Partida")
|
||||
logger.info("Respuesta SOAP exitosa, enviando documento")
|
||||
|
||||
# Actualizar status del partida
|
||||
# Enviar documento
|
||||
_file_name = f"vu_PT_{pedimento_app}_{partida.get('numero', '')}.xml"
|
||||
try:
|
||||
document_response = await partida_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||
pedimento=kwargs.get('pedimento').get('id'),
|
||||
file_name=_file_name,
|
||||
document_type=1,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error al enviar documento: {e}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error al guardar el documento",
|
||||
errors=[str(e)],
|
||||
metadata={
|
||||
"file_name": _file_name,
|
||||
"partida_numero": partida.get('numero')
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
logger.info("Documento enviado, actualizando status de Partida")
|
||||
|
||||
# Actualizar status de la partida
|
||||
try:
|
||||
partida_status_response = await change_partida_status(
|
||||
partida=kwargs.get('partida'),
|
||||
status=True,
|
||||
pedimento=kwargs.get('pedimento')
|
||||
)
|
||||
|
||||
logger.info(f"Partida {partida.get('numero', '')} procesado exitosamente")
|
||||
|
||||
# Asegurar que la respuesta sea serializable
|
||||
result = {
|
||||
"documento": document_response if document_response else None,
|
||||
"partida_update_response": partida_status_response if partida_status_response else None
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error procesando la partida: {str(e)}", exc_info=True)
|
||||
# Asegurar que no se retornen datos binarios en el error
|
||||
raise Exception(f"Error interno al procesar la partida: {str(e)}")
|
||||
logger.warning(f"Error al actualizar estado de la partida: {e}")
|
||||
# No fallamos aquí porque el documento ya se guardó exitosamente
|
||||
partida_status_response = None
|
||||
|
||||
logger.info(f"Partida {partida.get('numero', '')} procesada exitosamente")
|
||||
|
||||
return create_service_response(
|
||||
message=f"Partida {partida.get('numero', '')} procesada exitosamente",
|
||||
data={
|
||||
"documento": document_response,
|
||||
"partida_update_response": partida_status_response,
|
||||
"file_name": _file_name
|
||||
},
|
||||
metadata={
|
||||
"document_type": 1,
|
||||
"pedimento_app": pedimento_app,
|
||||
"organizacion": kwargs.get('pedimento').get('organizacion'),
|
||||
"content_type": "application/xml"
|
||||
}
|
||||
)
|
||||
|
||||
async def change_partida_status(partida: dict, status: bool, pedimento: dict):
|
||||
"""
|
||||
Actualiza el estado de una partida.
|
||||
"""
|
||||
if not partida or not pedimento:
|
||||
logger.error("Datos insuficientes para actualizar estado de partida")
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=create_error_response(
|
||||
message="Datos incompletos para actualizar estado",
|
||||
errors=["Faltan datos de partida o pedimento"],
|
||||
metadata={
|
||||
"has_partida": bool(partida),
|
||||
"has_pedimento": bool(pedimento)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
data = {
|
||||
"id": partida.get("id"),
|
||||
"numero_partida": partida.get("numero"),
|
||||
@@ -130,7 +189,18 @@ async def change_partida_status(partida: dict, status: bool, pedimento: dict):
|
||||
"pedimento": pedimento.get("id"),
|
||||
"organizacion": pedimento.get("organizacion"),
|
||||
}
|
||||
print(data)
|
||||
|
||||
response = await partida_rest_controller.put_partida(partida_id=partida.get("id"), data=data)
|
||||
|
||||
if not response:
|
||||
logger.error("Error al actualizar estado de la partida")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error al actualizar estado de la partida",
|
||||
errors=["No se recibió respuesta del servicio"],
|
||||
metadata={"partida_id": partida.get("id")}
|
||||
)
|
||||
)
|
||||
|
||||
return response
|
||||
@@ -2,6 +2,7 @@ from fastapi import APIRouter, BackgroundTasks, status, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from .schemas import PedimentoCompletoRequestSchema
|
||||
from .tasks import process_pedimento_completo_request
|
||||
from api.api_v2.modules.tasks.services import register_task
|
||||
import logging
|
||||
logger = logging.getLogger("app.api")
|
||||
|
||||
@@ -14,8 +15,16 @@ async def download_pedimento_completo(Pedimento: PedimentoCompletoRequestSchema)
|
||||
"""
|
||||
pedimento_dict = Pedimento.model_dump()
|
||||
|
||||
|
||||
# Ejecuta la tarea de Celery de forma asíncrona
|
||||
task = process_pedimento_completo_request.delay(pedimento_dict)
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando descarga de pedimento completo {pedimento_dict.get('pedimento', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=pedimento_dict.get('id'),
|
||||
organizacion_id=pedimento_dict.get('organizacion'),
|
||||
servicio=3 # 3 corresponde a "Pedimento Completo"
|
||||
)
|
||||
# Puedes devolver el ID de la tarea para consultar el estado después
|
||||
return {"status": "submitted", "detail": "La solicitud de descarga del pedimento completo ha sido enviada.", "task_id": task.id}
|
||||
|
||||
@@ -2,6 +2,7 @@ from fastapi import APIRouter, BackgroundTasks, status, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from .schemas import RemesaBaseSchema
|
||||
from .tasks import process_remesa_request
|
||||
from api.api_v2.modules.tasks.services import register_task
|
||||
import logging
|
||||
logger = logging.getLogger("app.api")
|
||||
|
||||
@@ -14,8 +15,16 @@ async def download_remesa(remesa_request: RemesaBaseSchema):
|
||||
"""
|
||||
remesa_dict = remesa_request.model_dump()
|
||||
|
||||
|
||||
# Ejecuta la tarea de Celery de forma asíncrona
|
||||
task = process_remesa_request.delay(remesa_dict)
|
||||
# Registrar la tarea en el servicio de seguimiento
|
||||
await register_task(
|
||||
task_id=task.id,
|
||||
message=f"Procesando descarga de remesa {remesa_dict.get('remesa', 'N/A')}",
|
||||
status="submitted",
|
||||
pedimento_id=remesa_dict.get('pedimento', {}).get('id'),
|
||||
organizacion_id=remesa_dict.get('pedimento', {}).get('organizacion'),
|
||||
servicio=5 # 5 corresponde a "Pedimento Remesas"
|
||||
)
|
||||
# Puedes devolver el ID de la tarea para consultar el estado después
|
||||
return {"status": "submitted", "detail": "La solicitud de descarga de la remesa ha sido enviada.", "task_id": task.id}
|
||||
|
||||
@@ -13,6 +13,7 @@ from .controllers import remesa_rest_controller, remesa_vu_controller, remesa_xm
|
||||
|
||||
|
||||
from utils.helpers import soap_error
|
||||
from ..common import create_service_response, create_error_response
|
||||
|
||||
# Logger configurado para el módulo
|
||||
logger = logging.getLogger("app.api")
|
||||
@@ -71,11 +72,8 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
|
||||
)
|
||||
# Generar nombre de archivo
|
||||
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}.xml"
|
||||
|
||||
# Enviar documento
|
||||
try:
|
||||
if soap_error(soap_response):
|
||||
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}.xml"
|
||||
if soap_error(soap_response):
|
||||
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_ERROR.xml"
|
||||
document_response = await remesa_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=pedimento_data.get('organizacion'),
|
||||
@@ -84,14 +82,16 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
|
||||
document_type=10,
|
||||
)
|
||||
raise HTTPException(status_code=500, detail="Error en la respuesta del servicio SOAP")
|
||||
else:
|
||||
document_response = await remesa_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=pedimento_data.get('organizacion'),
|
||||
pedimento=pedimento_data.get('id'),
|
||||
file_name=file_name,
|
||||
document_type=3,
|
||||
)
|
||||
# Enviar documento
|
||||
try:
|
||||
|
||||
document_response = await remesa_rest_controller.post_document(
|
||||
soap_response=soap_response,
|
||||
organizacion=pedimento_data.get('organizacion'),
|
||||
pedimento=pedimento_data.get('id'),
|
||||
file_name=file_name,
|
||||
document_type=3,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error al enviar documento: {e}")
|
||||
@@ -108,10 +108,19 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
|
||||
|
||||
logger.info(f"Remesa procesada exitosamente: {pedimento_data.get('pedimento')}")
|
||||
|
||||
return {
|
||||
"documento": document_response,
|
||||
"xml_content": remesas_data
|
||||
}
|
||||
return create_service_response(
|
||||
message="Remesa procesada exitosamente",
|
||||
data={
|
||||
"documento": document_response,
|
||||
"xml_content": remesas_data
|
||||
},
|
||||
metadata={
|
||||
"file_name": file_name,
|
||||
"document_type": 3,
|
||||
"pedimento_app": pedimento_data.get('pedimento_app'),
|
||||
"organizacion": pedimento_data.get('organizacion')
|
||||
}
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -170,7 +179,17 @@ async def post_remesa_data(**kwargs) -> Dict[str, Any]:
|
||||
|
||||
|
||||
logger.info("Procesamiento de pedimento completo finalizado")
|
||||
return result
|
||||
|
||||
# Crear respuesta estandarizada
|
||||
return create_service_response(
|
||||
success=True,
|
||||
message="Procesamiento de remesa completado",
|
||||
data=result,
|
||||
warnings=[result["coves_error"]] if result.get("coves_error") else None,
|
||||
metadata={
|
||||
"total_coves_procesados": len(result.get("coves_procesados", [])) if result.get("coves_procesados") else 0
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import logging
|
||||
import httpx
|
||||
from typing import Dict, Any
|
||||
from fastapi import HTTPException
|
||||
from ..common import create_error_response
|
||||
from core.config import settings
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
async def register_task(
|
||||
task_id: str,
|
||||
message: str,
|
||||
status: str,
|
||||
pedimento_id: str,
|
||||
organizacion_id: str,
|
||||
servicio: int
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Registra una tarea en el servicio de seguimiento.
|
||||
|
||||
Args:
|
||||
task_id: ID de la tarea de Celery
|
||||
message: Mensaje descriptivo de la tarea
|
||||
status: Estado actual de la tarea
|
||||
pedimento_id: ID del pedimento asociado
|
||||
organizacion_id: ID de la organización
|
||||
servicio: ID del tipo de servicio
|
||||
1: Estado de pedimento
|
||||
2: Listado de pedimentos
|
||||
3: Pedimento Completo
|
||||
4: Pedimento Partidas
|
||||
5: Pedimento Remesas
|
||||
6: Acuse
|
||||
7: EDocument
|
||||
8: Cove
|
||||
9: Acuse Cove
|
||||
|
||||
Returns:
|
||||
Dict con la respuesta del servicio
|
||||
"""
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Token {settings.API_TOKEN}"
|
||||
}
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
f"{settings.API_URL}/tasks/tasks/",
|
||||
json={
|
||||
"task_id": task_id,
|
||||
"message": message,
|
||||
"status": status,
|
||||
"pedimento": pedimento_id,
|
||||
"organizacion": organizacion_id,
|
||||
"servicio": servicio
|
||||
},
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPError as e:
|
||||
logger.error(f"Error al registrar tarea {task_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error al registrar la tarea",
|
||||
errors=[str(e)],
|
||||
metadata={
|
||||
"task_id": task_id,
|
||||
"status": status,
|
||||
"pedimento": pedimento_id
|
||||
}
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error inesperado al registrar tarea {task_id}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=create_error_response(
|
||||
message="Error inesperado al registrar la tarea",
|
||||
errors=[str(e)],
|
||||
metadata={"task_id": task_id}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -22,5 +22,8 @@ def soap_error(soap_response): # Testeado
|
||||
return True
|
||||
if "<mensaje>El Cove o Adenda no existe, no está firmado o no cuenta con la autorización para consultarlo</mensaje>" in soap_response.text:
|
||||
return True
|
||||
if "<ns2:mensaje>No hay información para la búsqueda solicitada</ns2:mensaje>" in soap_response.text:
|
||||
return True
|
||||
|
||||
# Aquí podrías agregar más lógica para verificar errores específicos en el XML
|
||||
return False
|
||||
Reference in New Issue
Block a user