fix: Se crean endpoints para mostrar la informacion de peticiones y respuestas de los webservices, en el area del auditor del sistema.
This commit is contained in:
@@ -14,6 +14,7 @@ from rest_framework.exceptions import ValidationError
|
||||
from .serializers import DocumentSerializer, FuenteSerializer, DocumentTypeSerializer
|
||||
from .models import Document, Fuente, DocumentType
|
||||
from ..customs.models import Pedimento
|
||||
from ..vucem.models import CredencialesImportador
|
||||
from api.organization.models import UsoAlmacenamiento
|
||||
from io import BytesIO
|
||||
import zipfile
|
||||
@@ -35,9 +36,91 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
import os
|
||||
from django.core.files.storage import default_storage
|
||||
from django.conf import settings
|
||||
import requests
|
||||
import re
|
||||
|
||||
from mixins.filtrado_organizacion import DocumentosFiltradosMixin
|
||||
|
||||
# Configuración de patrones y mapeos (definirlo fuera del try para reutilización)
|
||||
DOCUMENT_PATTERNS = {
|
||||
'REQUEST': {
|
||||
'VU_PC': (r".*VU_PC.*REQUEST\.xml$", 13, "Request Pedimento Completo VU"),
|
||||
'VU_ED': (r".*VU_ED.*REQUEST\.xml$", 21, "Request E-Document VU"),
|
||||
'VU_PT': (r".*VU_PT.*REQUEST\.xml$", 17, "Request Partidas VU"),
|
||||
'VU_AC_COVE': (r".*VU_AC_COVE.*REQUEST\.xml$", 23, "Request Acuses COVES VU"),
|
||||
'VU_COVE': (r".*VU_COVE.*REQUEST\.xml$", 19, "Request COVES VU"),
|
||||
'VU_RM': (r".*VU_RM.*REQUEST\.xml$", 15, "Request Remesas VU"),
|
||||
'VU_AC': (r".*VU_AC.*REQUEST\.xml$", 25, "Request Acuses VU"),
|
||||
},
|
||||
'ERROR': {
|
||||
'VU_PC': (r".*VU_PC.*ERROR\.xml$", 14, "Error Pedimento Completo VU"),
|
||||
'VU_ED': (r".*VU_ED.*ERROR\.xml$", 22, "Error E-Document VU"),
|
||||
'VU_PT': (r".*VU_PT.*ERROR\.xml$", 18, "Error Partidas VU"),
|
||||
'VU_AC_COVE': (r".*VU_AC_COVE.*ERROR\.xml$", 24, "Error Acuses COVES VU"),
|
||||
'VU_COVE': (r".*VU_COVE.*ERROR\.xml$", 20, "Error COVES VU"),
|
||||
'VU_RM': (r".*VU_RM.*ERROR\.xml$", 16, "Error Remesas VU"),
|
||||
'VU_AC': (r".*VU_AC.*ERROR\.xml$", 26, "Error Acuses VU"),
|
||||
}
|
||||
}
|
||||
|
||||
def eliminar_documentos_existentes(organizacion, nombre_sin_extension, pedimento_id):
|
||||
|
||||
"""Elimina documentos existentes con el mismo nombre base"""
|
||||
documentos_existentes = Document.objects.filter(
|
||||
archivo__icontains=nombre_sin_extension,
|
||||
organizacion=organizacion,
|
||||
pedimento_id=pedimento_id
|
||||
)
|
||||
|
||||
if not documentos_existentes.exists():
|
||||
return
|
||||
|
||||
for doc_existente in documentos_existentes:
|
||||
# Eliminar archivo físico si existe
|
||||
if doc_existente.archivo and os.path.exists(doc_existente.archivo.path):
|
||||
try:
|
||||
os.remove(doc_existente.archivo.path)
|
||||
except Exception as e:
|
||||
logger.error(f"Error al eliminar archivo físico: {e}")
|
||||
|
||||
# Eliminar registros de la base de datos
|
||||
documentos_existentes.delete()
|
||||
|
||||
def obtener_tipo_documento_por_patron(nombre_archivo, organizacion, pedimento_id):
|
||||
"""Determina el tipo de documento basado en patrones de nombre"""
|
||||
nombre_sin_extension = nombre_archivo.rsplit('.', 1)[0]
|
||||
|
||||
# Verificar patrones REQUEST
|
||||
for doc_key, (patron, type_id, descripcion) in DOCUMENT_PATTERNS['REQUEST'].items():
|
||||
if re.search(patron, nombre_archivo, re.IGNORECASE):
|
||||
try:
|
||||
# Eliminar documentos existentes
|
||||
eliminar_documentos_existentes(organizacion, nombre_sin_extension, pedimento_id)
|
||||
|
||||
# Obtener tipo de documento
|
||||
return DocumentType.objects.get(id=type_id)
|
||||
except DocumentType.DoesNotExist:
|
||||
raise ValidationError({
|
||||
"error": f"El tipo de documento '{descripcion}' no existe. Por favor, créelo primero."
|
||||
})
|
||||
|
||||
# Verificar patrones ERROR (si es necesario procesarlos)
|
||||
for doc_key, (patron, type_id, descripcion) in DOCUMENT_PATTERNS['ERROR'].items():
|
||||
if re.search(patron, nombre_archivo, re.IGNORECASE):
|
||||
try:
|
||||
# Eliminar documentos existentes
|
||||
eliminar_documentos_existentes(organizacion, nombre_sin_extension, pedimento_id)
|
||||
|
||||
# Obtener tipo de documento
|
||||
return DocumentType.objects.get(id=type_id)
|
||||
except DocumentType.DoesNotExist:
|
||||
raise ValidationError({
|
||||
"error": f"El tipo de documento '{descripcion}' no existe. Por favor, créelo primero."
|
||||
})
|
||||
|
||||
return None
|
||||
|
||||
class CustomPagination(PageNumberPagination):
|
||||
|
||||
"""
|
||||
@@ -144,12 +227,45 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
"codigo": "storage_limit_exceeded"
|
||||
}, code=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Guardar documento y actualizar espacio atómicamente
|
||||
documento = serializer.save(
|
||||
organizacion=organizacion,
|
||||
size=archivo.size,
|
||||
extension=archivo.name.split('.')[-1].lower()
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
pedimento_id = serializer.validated_data.get('pedimento').id if serializer.validated_data.get('pedimento') else None
|
||||
document_type = serializer.validated_data.get('document_type')
|
||||
|
||||
# Determinar el tipo de documento basado en el nombre del archivo
|
||||
detected_type = obtener_tipo_documento_por_patron(archivo.name, organizacion, pedimento_id)
|
||||
|
||||
if detected_type:
|
||||
document_type = detected_type
|
||||
else:
|
||||
# Lógica para archivos que no coinciden con los patrones conocidos
|
||||
logger.warning(f"No se encontró patrón para archivo: {archivo.name}")
|
||||
# Puedes mantener el document_type original o manejarlo de otra forma
|
||||
|
||||
except ValidationError as ve:
|
||||
raise ve
|
||||
except Exception as e:
|
||||
# Como fallback, intentar obtener cualquier DocumentType existente
|
||||
logger.error(f"Error al determinar el tipo de documento basado en el nombre del archivo: {e}")
|
||||
|
||||
try:
|
||||
|
||||
# Guardar documento y actualizar espacio atómicamente
|
||||
documento = serializer.save(
|
||||
document_type=document_type,
|
||||
organizacion=organizacion,
|
||||
size=archivo.size,
|
||||
extension=archivo.name.split('.')[-1].lower()
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Guardar documento y actualizar espacio atómicamente
|
||||
documento = serializer.save(
|
||||
organizacion=organizacion,
|
||||
size=archivo.size,
|
||||
extension=archivo.name.split('.')[-1].lower()
|
||||
)
|
||||
|
||||
uso.espacio_utilizado = nuevo_espacio_utilizado
|
||||
uso.save()
|
||||
@@ -833,7 +949,114 @@ class PedimentoDocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
queryset = queryset.filter(pedimento__pedimento_app=pedimento_numero)
|
||||
|
||||
return queryset
|
||||
class TriggerPedimentoCompletoView(APIView):
|
||||
"""
|
||||
Endpoint interno para disparar la descarga de pedimento completo
|
||||
en el microservicio FastAPI. Reenvía el payload tal cual y devuelve
|
||||
la respuesta del microservicio (normalmente un `task_id`).
|
||||
"""
|
||||
# permission_classes = [IsAuthenticated]
|
||||
permission_classes = [IsAuthenticated & (IsSuperUser | IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper )]
|
||||
|
||||
my_tags = ['Microservice - Pedimento Completo']
|
||||
|
||||
def post(self, request):
|
||||
|
||||
if not request.user.is_authenticated or not hasattr(request.user, 'organizacion'):
|
||||
return Response({"error": "Usuario no autenticado o sin organización"}, status=401)
|
||||
|
||||
# Validación mínima
|
||||
# if not payload.get('credencial') or not payload.get('pedimento_id'):
|
||||
# return Response({"error": "Se requieren 'credencial' y 'pedimento'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not request.data.get('pedimento_id'):
|
||||
return Response({"error": "Se requieren 'credencial' y 'pedimento'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
pedimento_id = request.data.get('pedimento_id')
|
||||
# Verificar que el pedimento existe y pertenece a la organización del usuario
|
||||
try:
|
||||
pedimento = Pedimento.objects.get(id=pedimento_id)
|
||||
|
||||
if not pedimento.contribuyente:
|
||||
return Response({"error": "El pedimento no tiene un contribuyente asociado"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
contribuyente_rfc = pedimento.contribuyente.rfc
|
||||
|
||||
payload = {
|
||||
"pedimento": {
|
||||
"id": str(pedimento.id),
|
||||
"pedimento": pedimento.pedimento,
|
||||
"pedimento_app": pedimento.pedimento_app,
|
||||
"aduana": pedimento.aduana,
|
||||
"patente": pedimento.patente,
|
||||
"contribuyente": contribuyente_rfc,
|
||||
"organizacion": str(pedimento.organizacion.id)
|
||||
},
|
||||
"credencial": {
|
||||
"id": "",
|
||||
"user": "",
|
||||
"password": "",
|
||||
"efirma": "",
|
||||
"key": "",
|
||||
"cer": "",
|
||||
"is_active": False,
|
||||
"organizacion": ""
|
||||
}
|
||||
}
|
||||
except Pedimento.DoesNotExist:
|
||||
return Response({"error": "Pedimento no encontrado"}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({"error": f"Error al buscar pedimento: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
try:
|
||||
credenciales = CredencialesImportador.objects.get(rfc=contribuyente_rfc)
|
||||
vucem = credenciales.vucem
|
||||
|
||||
# Obtener las rutas de los archivos, no los objetos FieldFile
|
||||
key_path = vucem.key.path if vucem.key else ""
|
||||
cer_path = vucem.cer.path if vucem.cer else ""
|
||||
|
||||
payload['credencial'] = {
|
||||
"id": str(credenciales.id),
|
||||
"user": vucem.usuario if vucem.usuario else "",
|
||||
"password": vucem.password if vucem.password else "",
|
||||
"efirma": vucem.efirma if vucem.efirma else "",
|
||||
"key": key_path,
|
||||
"cer": cer_path,
|
||||
"is_active": vucem.is_active if vucem.is_active else False,
|
||||
"organizacion": str(credenciales.organizacion.id) if credenciales.organizacion else ""
|
||||
}
|
||||
except CredencialesImportador.DoesNotExist:
|
||||
return Response({"error": "No se encontró credencial VUCEM para la organización del pedimento"}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({"error": f"Error al buscar credencial VUCEM: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
try:
|
||||
# Obtener la URL desde las variables de entorno
|
||||
api_url = os.getenv('SERVICE_API_URL_V2')
|
||||
logger.info(f"Usando MICROSERVICE_BASE_URL: {api_url}")
|
||||
endpoint = f"{api_url.rstrip('/')}/services/auditar_pedimento_completo"
|
||||
# endpoint = "http://localhost:8001/api/v2/services/auditar_pedimento_completo"
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error obteniendo MICROSERVICE_BASE_URL: {e}")
|
||||
return Response({"error": "Error obteniendo la URL del microservicio", "detail": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
except Exception as e:
|
||||
logger.error(f"Error inesperado obteniendo MICROSERVICE_BASE_URL: {e}")
|
||||
return Response({"error": "Error inesperado obteniendo la URL del microservicio", "detail": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
try:
|
||||
resp = requests.post(endpoint, json=payload, timeout=30)
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error comunicándose con microservice: {e}")
|
||||
return Response({"error": "No se pudo conectar con el microservicio", "detail": str(e)}, status=status.HTTP_502_BAD_GATEWAY)
|
||||
|
||||
try:
|
||||
content = resp.json()
|
||||
except ValueError:
|
||||
content = {"detail": resp.text}
|
||||
|
||||
return Response(content, status=resp.status_code)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user