diff --git a/api/api_v2/modules/acuses/routers.py b/api/api_v2/modules/acuses/routers.py
index 95d7612..8f3ee7a 100644
--- a/api/api_v2/modules/acuses/routers.py
+++ b/api/api_v2/modules/acuses/routers.py
@@ -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"}
@@ -39,5 +48,15 @@ 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)}
diff --git a/api/api_v2/modules/acuses/services.py b/api/api_v2/modules/acuses/services.py
index b9d843b..691409e 100644
--- a/api/api_v2/modules/acuses/services.py
+++ b/api/api_v2/modules/acuses/services.py
@@ -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.")
-
-
- pdf_bytes = _decode_acuse_base64_content(acuse_base64)
+ 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 = {
diff --git a/api/api_v2/modules/common/__init__.py b/api/api_v2/modules/common/__init__.py
new file mode 100644
index 0000000..138709b
--- /dev/null
+++ b/api/api_v2/modules/common/__init__.py
@@ -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']
\ No newline at end of file
diff --git a/api/api_v2/modules/common/response.py b/api/api_v2/modules/common/response.py
new file mode 100644
index 0000000..45e9926
--- /dev/null
+++ b/api/api_v2/modules/common/response.py
@@ -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
+ )
\ No newline at end of file
diff --git a/api/api_v2/modules/coves/routers.py b/api/api_v2/modules/coves/routers.py
index b09b2c6..56f664a 100644
--- a/api/api_v2/modules/coves/routers.py
+++ b/api/api_v2/modules/coves/routers.py
@@ -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_ids": task_ids, "status": "submitted", "total": len(task_ids)}
- return {"task_id": task.id, "coves_tasks_ids": task_ids, "status": "submitted"}
-
-
-@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)}
\ No newline at end of file
diff --git a/api/api_v2/modules/coves/services.py b/api/api_v2/modules/coves/services.py
index 8f86d1c..59b3dee 100644
--- a/api/api_v2/modules/coves/services.py
+++ b/api/api_v2/modules/coves/services.py
@@ -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:
diff --git a/api/api_v2/modules/edocs/routers.py b/api/api_v2/modules/edocs/routers.py
index d7a1752..b98cf40 100644
--- a/api/api_v2/modules/edocs/routers.py
+++ b/api/api_v2/modules/edocs/routers.py
@@ -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)}
\ No newline at end of file
diff --git a/api/api_v2/modules/edocs/services.py b/api/api_v2/modules/edocs/services.py
index 9d64cb7..193fae0 100644
--- a/api/api_v2/modules/edocs/services.py
+++ b/api/api_v2/modules/edocs/services.py
@@ -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'[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(' 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.
diff --git a/api/api_v2/modules/partidas/routers.py b/api/api_v2/modules/partidas/routers.py
index b33e5a5..967016b 100644
--- a/api/api_v2/modules/partidas/routers.py
+++ b/api/api_v2/modules/partidas/routers.py
@@ -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)}
\ No newline at end of file
diff --git a/api/api_v2/modules/partidas/services.py b/api/api_v2/modules/partidas/services.py
index 14013e5..112e246 100644
--- a/api/api_v2/modules/partidas/services.py
+++ b/api/api_v2/modules/partidas/services.py
@@ -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")
+ 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)
+ }
+ )
+ )
- logger.info(f"Procesando Partida: {partida} para usuario: {username}")
+ logger.info(f"Procesando Partida {partida.get('numero', 'N/A')} para usuario {username}")
- # Generar template SOAP
+ # 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_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'
+ }
- soap_headers = {
- 'Content-Type': 'text/xml; charset=utf-8'
- }
+ 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
+ )
- 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 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 not soap_response:
- raise Exception("No se recibió respuesta del servicio SOAP")
-
- if soap_error(soap_response):
+ 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("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 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 del partida
+ 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
\ No newline at end of file
diff --git a/api/api_v2/modules/pedimentos/routers.py b/api/api_v2/modules/pedimentos/routers.py
index a5f609d..2dae985 100644
--- a/api/api_v2/modules/pedimentos/routers.py
+++ b/api/api_v2/modules/pedimentos/routers.py
@@ -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}
diff --git a/api/api_v2/modules/remesas/routers.py b/api/api_v2/modules/remesas/routers.py
index e05ee2c..9bebabf 100644
--- a/api/api_v2/modules/remesas/routers.py
+++ b/api/api_v2/modules/remesas/routers.py
@@ -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}
diff --git a/api/api_v2/modules/remesas/services.py b/api/api_v2/modules/remesas/services.py
index f6b3c8a..9814a5a 100644
--- a/api/api_v2/modules/remesas/services.py
+++ b/api/api_v2/modules/remesas/services.py
@@ -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,15 +82,17 @@ 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}")
raise HTTPException(status_code=500, detail="Error al guardar documento")
@@ -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
+ }
+ )
diff --git a/api/api_v2/modules/tasks/services.py b/api/api_v2/modules/tasks/services.py
index e69de29..2edaea8 100644
--- a/api/api_v2/modules/tasks/services.py
+++ b/api/api_v2/modules/tasks/services.py
@@ -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}
+ )
+ )
diff --git a/utils/helpers.py b/utils/helpers.py
index ef939b1..8fef0ab 100644
--- a/utils/helpers.py
+++ b/utils/helpers.py
@@ -22,5 +22,8 @@ def soap_error(soap_response): # Testeado
return True
if "El Cove o Adenda no existe, no está firmado o no cuenta con la autorización para consultarlo" in soap_response.text:
return True
+ if "No hay información para la búsqueda solicitada" in soap_response.text:
+ return True
+
# Aquí podrías agregar más lógica para verificar errores específicos en el XML
return False
\ No newline at end of file