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 fastapi.responses import JSONResponse
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from api.api_v2.modules.authentication.services import get_current_user
|
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 .schemas import AcuseSchema, AcuseMasivoSchema
|
||||||
from .tasks import process_acuse_request
|
from .tasks import process_acuse_request
|
||||||
|
|
||||||
@@ -19,7 +18,17 @@ async def obtener_acuse(acuse_request: AcuseSchema):
|
|||||||
acuse_dict = acuse_request.model_dump()
|
acuse_dict = acuse_request.model_dump()
|
||||||
# Ejecuta la tarea de Celery de forma asíncrona
|
# Ejecuta la tarea de Celery de forma asíncrona
|
||||||
task = process_acuse_request.delay(acuse_dict)
|
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"}
|
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 = process_acuse_request.delay(acuse_dict)
|
||||||
task_ids.append(task.id)
|
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)}
|
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
from http.client import HTTPException
|
|
||||||
import base64
|
import base64
|
||||||
import re
|
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 .controllers import acuse_vu_controller, acuse_rest_controller
|
||||||
from utils.helpers import soap_error
|
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 = {
|
soap_headers = {
|
||||||
'Content-Type': 'text/xml; charset=utf-8',
|
'Content-Type': 'text/xml; charset=utf-8',
|
||||||
@@ -23,29 +29,68 @@ async def obtener_acuse(**kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if response is None:
|
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:
|
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)):
|
if soap_error(response):
|
||||||
acuse_base64 = _extract_acuse_data(response.text)
|
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:
|
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,
|
||||||
pdf_bytes = _decode_acuse_base64_content(acuse_base64)
|
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:
|
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
|
# Validar que el PDF sea válido
|
||||||
if not pdf_bytes.startswith(b'%PDF'):
|
if not pdf_bytes.startswith(b'%PDF'):
|
||||||
import logging
|
logger.error("El contenido decodificado no es un PDF válido")
|
||||||
logger = logging.getLogger("app.api")
|
raise HTTPException(
|
||||||
logger.warning("El contenido decodificado no parece ser un PDF válido")
|
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
|
# Mejorar el nombre del archivo usando todos los datos relevantes
|
||||||
pedimento = kwargs.get('pedimento', {})
|
pedimento = kwargs.get('pedimento', {})
|
||||||
@@ -65,21 +110,49 @@ async def obtener_acuse(**kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if rest_response is None:
|
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:
|
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(
|
acuse_update_response = await change_edocument_status(
|
||||||
edoc=kwargs.get('edoc'),
|
edoc=kwargs.get('edoc'),
|
||||||
status=True,
|
status=True,
|
||||||
pedimento=pedimento
|
pedimento=pedimento
|
||||||
)
|
)
|
||||||
return {
|
|
||||||
"document_response": rest_response,
|
return create_service_response(
|
||||||
"file_name": _file_name,
|
message="Acuse procesado exitosamente",
|
||||||
"pedimento": pedimento_num,
|
data={
|
||||||
"acuse_update_response": acuse_update_response
|
"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):
|
async def change_edocument_status(edoc: dict, status: bool, pedimento: dict):
|
||||||
data = {
|
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 fastapi import APIRouter, HTTPException
|
||||||
from .schemas import CoveListSchema, CoveRequestSchema
|
from .schemas import CoveListSchema, CoveRequestSchema
|
||||||
from typing import List
|
from typing import List, Dict, Any
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from .tasks import process_cove_request, process_acuse_cove_request
|
from .tasks import process_cove_request, process_acuse_cove_request
|
||||||
|
from ..tasks.services import register_task
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
# Aquí puedes definir tus endpoints relacionados con COVES usando el esquema CoveBaseSchema
|
# 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):
|
async def get_cove(cove: CoveRequestSchema):
|
||||||
# Lógica para obtener un COVE
|
"""Endpoint para obtener un COVE específico."""
|
||||||
task = process_cove_request.delay(cove.model_dump())
|
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"}
|
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):
|
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 = []
|
task_ids = []
|
||||||
coves_dict = coves_request.model_dump()
|
coves_dict = coves_request.model_dump()
|
||||||
|
pedimento = coves_dict.get('pedimento', {})
|
||||||
|
|
||||||
for cove in coves_dict.get('coves', []):
|
for cove in coves_dict.get('coves', []):
|
||||||
cove_dict = {
|
cove_dict = {
|
||||||
"cove": cove,
|
"cove": cove,
|
||||||
"pedimento": coves_dict.get('pedimento'),
|
"pedimento": pedimento,
|
||||||
"credencial": coves_dict.get('credencial')
|
"credencial": coves_dict.get('credencial')
|
||||||
}
|
}
|
||||||
task = process_cove_request.delay(cove_dict)
|
task = process_cove_request.delay(cove_dict)
|
||||||
task_ids.append(task.id)
|
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[str, Any])
|
||||||
|
|
||||||
|
|
||||||
@router.post("/services/acuse/cove/", response_model=dict)
|
|
||||||
async def get_acuse_cove(cove: CoveRequestSchema):
|
async def get_acuse_cove(cove: CoveRequestSchema):
|
||||||
# Lógica para obtener un COVE
|
"""Endpoint para obtener el acuse de un COVE específico."""
|
||||||
task = process_acuse_cove_request.delay(cove.model_dump())
|
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"}
|
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):
|
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 = []
|
task_ids = []
|
||||||
coves_dict = coves_request.model_dump()
|
coves_dict = coves_request.model_dump()
|
||||||
|
pedimento = coves_dict.get('pedimento', {})
|
||||||
|
|
||||||
for cove in coves_dict.get('coves', []):
|
for cove in coves_dict.get('coves', []):
|
||||||
acuse_dict = {
|
acuse_dict = {
|
||||||
"cove": cove,
|
"cove": cove,
|
||||||
"pedimento": coves_dict.get('pedimento'),
|
"pedimento": pedimento,
|
||||||
"credencial": coves_dict.get('credencial')
|
"credencial": coves_dict.get('credencial')
|
||||||
}
|
}
|
||||||
task = process_acuse_cove_request.delay(acuse_dict)
|
task = process_acuse_cove_request.delay(acuse_dict)
|
||||||
task_ids.append(task.id)
|
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)}
|
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||||
@@ -2,17 +2,20 @@ import base64
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import tempfile
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from controllers.RESTController import rest_controller
|
from controllers.RESTController import rest_controller
|
||||||
from controllers.SOAPController import soap_controller
|
from controllers.SOAPController import soap_controller
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from utils.helpers import soap_error
|
from utils.helpers import soap_error
|
||||||
from .controllers import coves_vu_controller, coves_rest_controller
|
from .controllers import coves_vu_controller, coves_rest_controller
|
||||||
|
from ..common import create_service_response, create_error_response
|
||||||
|
|
||||||
# Logger para el módulo
|
# Logger para el módulo
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -42,8 +45,26 @@ async def consume_ws_get_cove(**kwargs):
|
|||||||
cove = kwargs['cove'].get('cove', None)
|
cove = kwargs['cove'].get('cove', None)
|
||||||
|
|
||||||
if not credenciales or not username or not cove:
|
if not credenciales or not username or not cove:
|
||||||
raise Exception(
|
missing = []
|
||||||
"Credenciales o COVE no proporcionados correctamente")
|
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}")
|
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")
|
logger.info(f"COVE {cove} procesado exitosamente")
|
||||||
|
|
||||||
# Asegurar que la respuesta sea serializable
|
return create_service_response(
|
||||||
result = {
|
message=f"COVE {cove} procesado exitosamente",
|
||||||
"documento": document_response if document_response else None,
|
data={
|
||||||
"cove_update_response": cove_status_response if cove_status_response else None
|
"documento": document_response if document_response else None,
|
||||||
}
|
"cove_update_response": cove_status_response if cove_status_response else None
|
||||||
|
},
|
||||||
return result
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error procesando COVE: {str(e)}", exc_info=True)
|
logger.error(f"Error procesando COVE: {str(e)}", exc_info=True)
|
||||||
# Asegurar que no se retornen datos binarios en el error
|
raise HTTPException(
|
||||||
raise Exception(f"Error interno al procesar COVE: {str(e)}")
|
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):
|
async def consume_ws_get_acuse_cove(**kwargs):
|
||||||
@@ -155,20 +196,57 @@ async def consume_ws_get_acuse_cove(**kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if response is None:
|
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:
|
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):
|
if soap_error(response):
|
||||||
rest_response = await coves_rest_controller.post_document(
|
error_file_name = f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml"
|
||||||
soap_response=response,
|
try:
|
||||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
rest_response = await coves_rest_controller.post_document(
|
||||||
pedimento=kwargs.get('pedimento').get('id'),
|
soap_response=response,
|
||||||
file_name=f"vu_AC_COVE_{kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')}_{kwargs['cove'].get('cove', 'N/A')}_ERROR.xml",
|
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||||
document_type=10,
|
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)):
|
if (response) and (not soap_error(response)):
|
||||||
logger.debug(f"Respuesta SOAP recibida, extrayendo acuse...")
|
logger.debug(f"Respuesta SOAP recibida, extrayendo acuse...")
|
||||||
acuse_base64 = _extract_acuse_data(response.text)
|
acuse_base64 = _extract_acuse_data(response.text)
|
||||||
@@ -224,12 +302,22 @@ async def consume_ws_get_acuse_cove(**kwargs):
|
|||||||
pedimento=kwargs.get('pedimento')
|
pedimento=kwargs.get('pedimento')
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return create_service_response(
|
||||||
"document_response": rest_response,
|
message="Acuse de COVE procesado exitosamente",
|
||||||
"file_name": _file_name,
|
data={
|
||||||
"pedimento": pedimento_num,
|
"document_response": rest_response,
|
||||||
"acuse_update": acuse_status
|
"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
|
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
|
return firma, certificado, tmp_key_path
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(f"Error obteniendo certificado/llave o generando firma: {e}")
|
||||||
f"Error obteniendo certificado/llave o generando firma: {e}")
|
|
||||||
# Limpiar archivo temporal si existe
|
# Limpiar archivo temporal si existe
|
||||||
if 'tmp_key_path' in locals() and os.path.exists(tmp_key_path):
|
if 'tmp_key_path' in locals() and os.path.exists(tmp_key_path):
|
||||||
try:
|
try:
|
||||||
os.remove(tmp_key_path)
|
os.remove(tmp_key_path)
|
||||||
except:
|
except Exception as cleanup_error:
|
||||||
pass
|
logger.warning(f"Error al limpiar archivo temporal: {cleanup_error}")
|
||||||
raise Exception(f"Error en fetch_sign_and_cer: {str(e)}")
|
|
||||||
|
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:
|
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 .schemas import EdocumentsSchema, EdocumentsMasivoSchema
|
||||||
from .tasks import process_edoc_download_request
|
from .tasks import process_edoc_download_request
|
||||||
from api.api_v2.modules.authentication.services import get_current_user
|
from api.api_v2.modules.authentication.services import get_current_user
|
||||||
|
from api.api_v2.modules.tasks.services import register_task
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -17,6 +18,15 @@ async def download_edoc(edoc_request: EdocumentsSchema):
|
|||||||
edoc_dict = edoc_request.model_dump()
|
edoc_dict = edoc_request.model_dump()
|
||||||
# Ejecuta la tarea de Celery de forma asíncrona
|
# Ejecuta la tarea de Celery de forma asíncrona
|
||||||
task = process_edoc_download_request.delay(edoc_dict)
|
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
|
# Devuelve el ID de la tarea
|
||||||
return {"task_id": task.id, "status": "submitted"}
|
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 = process_edoc_download_request.delay(edoc_dict)
|
||||||
task_ids.append(task.id)
|
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)}
|
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||||
@@ -1,68 +1,22 @@
|
|||||||
from http.client import HTTPException
|
|
||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from utils.helpers import soap_error
|
from utils.helpers import soap_error
|
||||||
from .controllers import edocs_rest_controller, edocs_vu_controller
|
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")
|
logger = logging.getLogger("app.api")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# --- FUNCIONES AUXILIARES ---
|
# --- 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:
|
def _get_file_name(**kwargs) -> str:
|
||||||
pedimento = kwargs.get('pedimento', {})
|
pedimento = kwargs.get('pedimento', {})
|
||||||
@@ -89,25 +43,102 @@ async def obtener_edoc(**kwargs):
|
|||||||
data=soap_xml,
|
data=soap_xml,
|
||||||
headers=soap_headers
|
headers=soap_headers
|
||||||
)
|
)
|
||||||
|
# Validar respuesta del servicio SOAP
|
||||||
if response is None:
|
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:
|
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):
|
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:
|
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']
|
pdf_bytes = edoc_base64['pdf_bytes']
|
||||||
if not 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'):
|
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', {})
|
pedimento = kwargs.get('pedimento', {})
|
||||||
numero_documento = kwargs['edoc'].get('numero_edocument', '')
|
numero_documento = kwargs['edoc'].get('numero_edocument', '')
|
||||||
@@ -116,12 +147,8 @@ async def obtener_edoc(**kwargs):
|
|||||||
organizacion = pedimento.get("organizacion", None)
|
organizacion = pedimento.get("organizacion", None)
|
||||||
pedimento_id = pedimento.get("id", None)
|
pedimento_id = pedimento.get("id", None)
|
||||||
|
|
||||||
try:
|
# No guardaremos el archivo localmente por seguridad
|
||||||
with open(_file_name, "wb") as f:
|
logger.debug(f"Procesando documento {numero_documento} para pedimento {pedimento_id}")
|
||||||
f.write(pdf_bytes)
|
|
||||||
logger.info(f"PDF guardado localmente en {_file_name}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error guardando el PDF localmente: {e}")
|
|
||||||
|
|
||||||
rest_response = await edocs_rest_controller.post_document(
|
rest_response = await edocs_rest_controller.post_document(
|
||||||
binary_content=pdf_bytes,
|
binary_content=pdf_bytes,
|
||||||
@@ -132,26 +159,61 @@ async def obtener_edoc(**kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if rest_response is None:
|
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:
|
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")
|
logger.info("Documento enviado, actualizando status de Edoc")
|
||||||
|
|
||||||
edoc_status_response = await change_edocument_status(
|
try:
|
||||||
edoc=doc,
|
edoc_status_response = await change_edocument_status(
|
||||||
status=True,
|
edoc=doc,
|
||||||
pedimento=pedimento
|
status=True,
|
||||||
)
|
pedimento=pedimento
|
||||||
|
)
|
||||||
print(edoc_status_response)
|
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 {
|
logger.info(f"E-document {numero_documento} procesado exitosamente")
|
||||||
"document_response": rest_response,
|
|
||||||
"file_name": _file_name,
|
return create_service_response(
|
||||||
"numero_documento": numero_documento,
|
message=f"E-document {numero_documento} procesado exitosamente",
|
||||||
"edoc_update_response": edoc_status_response if edoc_status_response else None
|
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):
|
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
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def extract_pdf_bytes_from_xml_content(xml_content: str):
|
def extract_pdf_bytes_from_xml_content(xml_content: str):
|
||||||
"""
|
"""
|
||||||
Extrae el PDF y metadatos desde un string XML.
|
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.authentication.services import get_current_user
|
||||||
|
from api.api_v2.modules.tasks.services import register_task
|
||||||
from .schemas import PartidaRequestSchema, PartidaListSchema
|
from .schemas import PartidaRequestSchema, PartidaListSchema
|
||||||
from .tasks import process_partida_request
|
from .tasks import process_partida_request
|
||||||
|
|
||||||
@@ -19,6 +20,15 @@ async def obtener_partida(partida_request: PartidaRequestSchema):
|
|||||||
acuse_dict = partida_request.model_dump()
|
acuse_dict = partida_request.model_dump()
|
||||||
# Ejecuta la tarea de Celery de forma asíncrona
|
# Ejecuta la tarea de Celery de forma asíncrona
|
||||||
task = process_partida_request.delay(acuse_dict)
|
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
|
# Puedes devolver el ID de la tarea para consultar el estado después
|
||||||
return {"task_id": task.id, "status": "submitted"}
|
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 = process_partida_request.delay(partida_dict)
|
||||||
task_ids.append(task.id)
|
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)}
|
return {"task_ids": task_ids, "status": "submitted", "total": len(task_ids)}
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
import base64
|
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
from fastapi import HTTPException
|
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 utils.helpers import soap_error
|
||||||
from .controllers import partida_rest_controller, partida_vu_controller
|
from .controllers import partida_rest_controller, partida_vu_controller
|
||||||
|
from ..common import create_service_response, create_error_response
|
||||||
|
|
||||||
# Logger para el módulo
|
# Logger para el módulo
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -30,99 +20,168 @@ async def consume_ws_get_partida(**kwargs):
|
|||||||
Raises:
|
Raises:
|
||||||
Exception: Si hay errores en el procesamiento
|
Exception: Si hay errores en el procesamiento
|
||||||
"""
|
"""
|
||||||
try:
|
logger.info("Iniciando procesamiento de partidas")
|
||||||
logger.info("Iniciando procesamiento de partidas")
|
credenciales = kwargs.get('credencial')
|
||||||
credenciales = kwargs.get('credencial')
|
username = credenciales.get('user')
|
||||||
username = credenciales.get('user')
|
pedimento_app = kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')
|
||||||
pedimento_app = kwargs.get('pedimento', {}).get('pedimento_app', 'N/A')
|
partida = kwargs.get('partida', {})
|
||||||
partida = kwargs.get('partida', {})
|
|
||||||
|
|
||||||
if not credenciales or not username or not partida:
|
if not credenciales or not username or not partida:
|
||||||
raise Exception("Credenciales o Partida no proporcionados correctamente")
|
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(
|
soap_headers = {
|
||||||
username=username,
|
'Content-Type': 'text/xml; charset=utf-8'
|
||||||
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 = {
|
logger.info("Enviando petición SOAP a VUCEM")
|
||||||
'Content-Type': 'text/xml; charset=utf-8'
|
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")
|
if not soap_response:
|
||||||
soap_response = await partida_vu_controller.make_request_async(
|
logger.error("No se recibió respuesta del servicio SOAP")
|
||||||
"/ventanilla-ws-pedimentos/ConsultarPartidaService",
|
raise HTTPException(
|
||||||
data=soap_xml,
|
status_code=500,
|
||||||
headers=soap_headers
|
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):
|
||||||
if not soap_response:
|
error_file_name = f"vu_PT_{pedimento_app}_{partida.get('numero', '')}_ERROR.xml"
|
||||||
raise Exception("No se recibió respuesta del servicio SOAP")
|
try:
|
||||||
|
|
||||||
if soap_error(soap_response):
|
|
||||||
document_response = await partida_rest_controller.post_document(
|
document_response = await partida_rest_controller.post_document(
|
||||||
soap_response=soap_response,
|
soap_response=soap_response,
|
||||||
organizacion=kwargs.get('pedimento').get('organizacion'),
|
organizacion=kwargs.get('pedimento').get('organizacion'),
|
||||||
pedimento=kwargs.get('pedimento').get('id'),
|
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,
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error detectado en la respuesta SOAP: {str(e)}")
|
logger.error(f"Error al guardar la respuesta de error: {e}")
|
||||||
raise Exception(f"Error en la respuesta SOAP: {str(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")
|
||||||
logger.info("Documento enviado, actualizando status de Partida")
|
|
||||||
|
# Actualizar status de la partida
|
||||||
# Actualizar status del partida
|
try:
|
||||||
partida_status_response = await change_partida_status(
|
partida_status_response = await change_partida_status(
|
||||||
partida=kwargs.get('partida'),
|
partida=kwargs.get('partida'),
|
||||||
status=True,
|
status=True,
|
||||||
pedimento=kwargs.get('pedimento')
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error procesando la partida: {str(e)}", exc_info=True)
|
logger.warning(f"Error al actualizar estado de la partida: {e}")
|
||||||
# Asegurar que no se retornen datos binarios en el error
|
# No fallamos aquí porque el documento ya se guardó exitosamente
|
||||||
raise Exception(f"Error interno al procesar la partida: {str(e)}")
|
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):
|
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 = {
|
data = {
|
||||||
"id": partida.get("id"),
|
"id": partida.get("id"),
|
||||||
"numero_partida": partida.get("numero"),
|
"numero_partida": partida.get("numero"),
|
||||||
@@ -130,7 +189,18 @@ async def change_partida_status(partida: dict, status: bool, pedimento: dict):
|
|||||||
"pedimento": pedimento.get("id"),
|
"pedimento": pedimento.get("id"),
|
||||||
"organizacion": pedimento.get("organizacion"),
|
"organizacion": pedimento.get("organizacion"),
|
||||||
}
|
}
|
||||||
print(data)
|
|
||||||
response = await partida_rest_controller.put_partida(partida_id=partida.get("id"), data=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
|
return response
|
||||||
@@ -2,6 +2,7 @@ from fastapi import APIRouter, BackgroundTasks, status, HTTPException
|
|||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from .schemas import PedimentoCompletoRequestSchema
|
from .schemas import PedimentoCompletoRequestSchema
|
||||||
from .tasks import process_pedimento_completo_request
|
from .tasks import process_pedimento_completo_request
|
||||||
|
from api.api_v2.modules.tasks.services import register_task
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("app.api")
|
logger = logging.getLogger("app.api")
|
||||||
|
|
||||||
@@ -14,8 +15,16 @@ async def download_pedimento_completo(Pedimento: PedimentoCompletoRequestSchema)
|
|||||||
"""
|
"""
|
||||||
pedimento_dict = Pedimento.model_dump()
|
pedimento_dict = Pedimento.model_dump()
|
||||||
|
|
||||||
|
|
||||||
# Ejecuta la tarea de Celery de forma asíncrona
|
# Ejecuta la tarea de Celery de forma asíncrona
|
||||||
task = process_pedimento_completo_request.delay(pedimento_dict)
|
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
|
# 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}
|
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 fastapi.responses import JSONResponse
|
||||||
from .schemas import RemesaBaseSchema
|
from .schemas import RemesaBaseSchema
|
||||||
from .tasks import process_remesa_request
|
from .tasks import process_remesa_request
|
||||||
|
from api.api_v2.modules.tasks.services import register_task
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("app.api")
|
logger = logging.getLogger("app.api")
|
||||||
|
|
||||||
@@ -14,8 +15,16 @@ async def download_remesa(remesa_request: RemesaBaseSchema):
|
|||||||
"""
|
"""
|
||||||
remesa_dict = remesa_request.model_dump()
|
remesa_dict = remesa_request.model_dump()
|
||||||
|
|
||||||
|
|
||||||
# Ejecuta la tarea de Celery de forma asíncrona
|
# Ejecuta la tarea de Celery de forma asíncrona
|
||||||
task = process_remesa_request.delay(remesa_dict)
|
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
|
# 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}
|
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 utils.helpers import soap_error
|
||||||
|
from ..common import create_service_response, create_error_response
|
||||||
|
|
||||||
# Logger configurado para el módulo
|
# Logger configurado para el módulo
|
||||||
logger = logging.getLogger("app.api")
|
logger = logging.getLogger("app.api")
|
||||||
@@ -71,11 +72,8 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
|
|||||||
)
|
)
|
||||||
# Generar nombre de archivo
|
# Generar nombre de archivo
|
||||||
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}.xml"
|
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}.xml"
|
||||||
|
if soap_error(soap_response):
|
||||||
# Enviar documento
|
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}_ERROR.xml"
|
||||||
try:
|
|
||||||
if soap_error(soap_response):
|
|
||||||
file_name = f"vu_RM_{pedimento_data.get('pedimento_app', 'unknown')}.xml"
|
|
||||||
document_response = await remesa_rest_controller.post_document(
|
document_response = await remesa_rest_controller.post_document(
|
||||||
soap_response=soap_response,
|
soap_response=soap_response,
|
||||||
organizacion=pedimento_data.get('organizacion'),
|
organizacion=pedimento_data.get('organizacion'),
|
||||||
@@ -84,15 +82,17 @@ async def obtener_remesa(**kwargs) -> Dict[str, Any]:
|
|||||||
document_type=10,
|
document_type=10,
|
||||||
)
|
)
|
||||||
raise HTTPException(status_code=500, detail="Error en la respuesta del servicio SOAP")
|
raise HTTPException(status_code=500, detail="Error en la respuesta del servicio SOAP")
|
||||||
else:
|
# Enviar documento
|
||||||
document_response = await remesa_rest_controller.post_document(
|
try:
|
||||||
soap_response=soap_response,
|
|
||||||
organizacion=pedimento_data.get('organizacion'),
|
document_response = await remesa_rest_controller.post_document(
|
||||||
pedimento=pedimento_data.get('id'),
|
soap_response=soap_response,
|
||||||
file_name=file_name,
|
organizacion=pedimento_data.get('organizacion'),
|
||||||
document_type=3,
|
pedimento=pedimento_data.get('id'),
|
||||||
)
|
file_name=file_name,
|
||||||
|
document_type=3,
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error al enviar documento: {e}")
|
logger.error(f"Error al enviar documento: {e}")
|
||||||
raise HTTPException(status_code=500, detail="Error al guardar documento")
|
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')}")
|
logger.info(f"Remesa procesada exitosamente: {pedimento_data.get('pedimento')}")
|
||||||
|
|
||||||
return {
|
return create_service_response(
|
||||||
"documento": document_response,
|
message="Remesa procesada exitosamente",
|
||||||
"xml_content": remesas_data
|
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:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
@@ -170,7 +179,17 @@ async def post_remesa_data(**kwargs) -> Dict[str, Any]:
|
|||||||
|
|
||||||
|
|
||||||
logger.info("Procesamiento de pedimento completo finalizado")
|
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
|
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:
|
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
|
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
|
# Aquí podrías agregar más lógica para verificar errores específicos en el XML
|
||||||
return False
|
return False
|
||||||
Reference in New Issue
Block a user