fix/T2025-09-007 corregir documentos duplicados

This commit is contained in:
Dulce
2026-05-18 11:55:46 -06:00
parent 8cc0b9f573
commit c2ae752932
4 changed files with 707 additions and 407 deletions

View File

@@ -1,3 +1,4 @@
from api.utils.storage_service import storage_service
from config.settings import SERVICE_API_URL
from django.shortcuts import render
from rest_framework import viewsets
@@ -61,7 +62,6 @@ except ImportError:
# Importar tarea de procesamiento de pedimento (Celery)
from api.customs.tasks.microservice import procesar_pedimento_completo_individual
from api.utils.storage_service import storage_service
def get_available_extractors():
"""
@@ -394,6 +394,131 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-partidas')
def procesar_partidas(self, request, pk=None):
"""
Acción para disparar el procesamiento de un partidas de un pedimento existente.
Dispara la tarea `procesar_partidas_individual` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
from api.customs.tasks import microservice_v2
# Usar el nombre del servicio de Docker Compose en lugar de localhost
task = microservice_v2.procesar_partidas_pedimento.delay(pedimento.id)
# Verificar si la respuesta fue exitosa
if task.id:
return Response({"status": "Iniciando Procesamiento de Partidas", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "El Servicio respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-coves')
def procesar_coves(self, request, pk=None):
"""
Acción para disparar el procesamiento de un cove de un pedimento existente.
Dispara la tarea `procesar_coves_individual` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
from api.customs.tasks import microservice_v2
# Usar el nombre del servicio de Docker Compose en lugar de localhost
task = microservice_v2.procesar_coves_pedimento.delay(pedimento.id)
# Verificar si la respuesta fue exitosa
if task.id:
return Response({"status": "Iniciando Procesamiento de COVES", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "Servicio API respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-acuse-coves')
def procesar_acuse_coves(self, request, pk=None):
"""
Acción para disparar el procesamiento de un acuse cove de un pedimento existente.
Dispara la tarea `procesar_acuse_coves_individual` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
# Usar el nombre del servicio de Docker Compose en lugar de localhost
from api.customs.tasks import microservice_v2
# Usar el nombre del servicio de Docker Compose en lugar de localhost
task = microservice_v2.procesar_acuse_coves_pedimento.delay(pedimento.id)
# Verificar si la respuesta fue exitosa
if task.id:
return Response({"status": "Iniciando Procesamiento de Acuse COVES", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "Servicio API respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-edocuments')
def procesar_edocs(self, request, pk=None):
"""
Acción para disparar el procesamiento de un edocuments de un pedimento existente.
Dispara la tarea `procesar_edocuments_individual` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
# Usar el nombre del servicio de Docker Compose en lugar de localhost
from api.customs.tasks import microservice_v2
# Usar el nombre del servicio de Docker Compose en lugar de localhost
task = microservice_v2.procesar_edocs_pedimento.delay(pedimento.id)
# Verificar si la respuesta fue exitosa
if task.id:
return Response({"status": "Iniciando Procesamiento de EDOCS", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "Servicio API respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-acuses')
def procesar_acuses(self, request, pk=None):
"""
Acción para disparar el procesamiento de un acuses de un pedimento existente.
Dispara la tarea `procesar_acuses_individual` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
from api.customs.tasks import microservice_v2
# Usar el nombre del servicio de Docker Compose en lugar de localhost
task = microservice_v2.procesar_acuses_pedimento.delay(pedimento.id)
# Verificar si la respuesta fue exitosa
if task.id:
return Response({"status": "Iniciando Procesamiento de Acuses", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "Servicio API respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=True, methods=['post'], url_path='procesar-remesas')
def procesar_remesas(self, request, pk=None):
"""
Acción para disparar el procesamiento de remesas de un pedimento existente.
Dispara la tarea `procesar_remesas_pedimento` de forma asíncrona
y devuelve el `task_id`.
"""
pedimento = self.get_object()
try:
from api.customs.tasks import microservice_v2
task = microservice_v2.procesar_remesas_pedimento.delay(pedimento.id)
if task.id:
return Response({"status": "Iniciando Procesamiento de Remesas", "task_id": task.id}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"status": "Servicio API respondió con error", "task_id": 0}, status=status.HTTP_202_ACCEPTED)
except Exception as e:
return Response({"error": f"Error inesperado al llamar al servicio API: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(detail=False, methods=['post'], url_path='bulk-delete')
def bulk_delete(self, request):
import traceback
@@ -657,11 +782,7 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
"contribuyente": existing_pedimento.contribuyente.rfc if existing_pedimento.contribuyente else None,
"archivo_original": archivo.name
})
# NO procesamos este archivo, pasamos al siguiente
continue
# Si el pedimento no existe, continuar con el procesamiento normal
print("📝 Pedimento no existe, continuando con procesamiento...")
# Continuar al procesamiento de documentos del pedimento existente
# Crear subdirectorio para cada archivo usando el nombre del archivo sin extensión
sub_dir = os.path.join(temp_dir, archivo_name_sin_extension)
@@ -713,56 +834,59 @@ class ViewSetPedimento(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
f.write(chunk)
print(f"Archivo individual {archivo.name} guardado en sub_dir:", archivo_path)
# Ahora crear el pedimento (ya verificamos que no existe)
try:
print("🔄 Iniciando creación de pedimento...")
# Obtener o crear el importador
print(f"🏢 Buscando/creando importador con RFC: {contribuyente}")
importador, created = Importador.objects.get_or_create(
rfc=contribuyente,
defaults={
'nombre': f"Importador {contribuyente}",
'organizacion': organizacion
}
)
if created:
print(f"✅ Importador creado: {importador.rfc} - {importador.nombre}")
else:
print(f"♻️ Importador existente: {importador.rfc} - {importador.nombre}")
pedimento = Pedimento.objects.create(
organizacion=organizacion,
contribuyente=importador,
# pedimento=int(pedimento_num),
pedimento=pedimento_num,
aduana=aduana,
# aduana=int(aduana),
# patente=int(patente),
patente=patente,
fecha_pago=fecha_pago,
pedimento_app=pedimento_app,
agente_aduanal=f"Agente {patente}", # Valor por defecto
clave_pedimento="A1" # Valor por defecto
)
print(f"✅ Pedimento creado exitosamente: ID {pedimento.id}, pedimento_app: {pedimento_app}")
created_pedimentos.append({
"id": str(pedimento.id),
"pedimento_app": pedimento_app,
"contribuyente": importador.rfc,
"contribuyente_nombre": importador.nombre,
"archivo_original": archivo.name
})
except Exception as e:
print(f"❌ Error al crear pedimento: {str(e)}")
failed_files.append({
"archivo_original": archivo.name,
"error": f"Error al crear pedimento: {str(e)}"
})
continue
if existing_pedimento:
pedimento = existing_pedimento
else:
# Crear el pedimento nuevo
try:
print("🔄 Iniciando creación de pedimento...")
# Obtener o crear el importador
print(f"🏢 Buscando/creando importador con RFC: {contribuyente}")
importador, created = Importador.objects.get_or_create(
rfc=contribuyente,
defaults={
'nombre': f"Importador {contribuyente}",
'organizacion': organizacion
}
)
if created:
print(f"✅ Importador creado: {importador.rfc} - {importador.nombre}")
else:
print(f"♻️ Importador existente: {importador.rfc} - {importador.nombre}")
pedimento = Pedimento.objects.create(
organizacion=organizacion,
contribuyente=importador,
# pedimento=int(pedimento_num),
pedimento=pedimento_num,
aduana=aduana,
# aduana=int(aduana),
# patente=int(patente),
patente=patente,
fecha_pago=fecha_pago,
pedimento_app=pedimento_app,
agente_aduanal=f"Agente {patente}", # Valor por defecto
clave_pedimento="A1" # Valor por defecto
)
print(f"✅ Pedimento creado exitosamente: ID {pedimento.id}, pedimento_app: {pedimento_app}")
created_pedimentos.append({
"id": str(pedimento.id),
"pedimento_app": pedimento_app,
"contribuyente": importador.rfc,
"contribuyente_nombre": importador.nombre,
"archivo_original": archivo.name
})
except Exception as e:
print(f"Error al crear pedimento: {str(e)}")
failed_files.append({
"archivo_original": archivo.name,
"error": f"Error al crear pedimento: {str(e)}"
})
continue
# Procesar documentos dentro del directorio
print("Procesando documentos del directorio...")
@@ -2248,6 +2372,7 @@ class ViewSetEDocument(LoggingMixin, viewsets.ModelViewSet, OrganizacionFiltrada
serializer.save()
return
print(f"self.request.user.groups >>>> {self.request.user.groups}")
if (self.request.user.groups.filter(name='developer').exists() or self.request.user.groups.filter(name='admin').exists() or self.request.user.groups.filter(name='user').exists()) and self.request.user.groups.filter(name='Agente Aduanal').exists():
# Para usuarios normales, usar siempre su organización
if not hasattr(self.request.user, 'organizacion') or not self.request.user.organizacion:
@@ -2355,6 +2480,15 @@ class ImportadorViewSet(viewsets.ModelViewSet, OrganizacionFiltradaMixin):
model = Importador
def get_queryset(self):
user = self.request.user
grupos = user.groups.values_list('name', flat=True)
if user.is_superuser:
return Importador.objects.all()
if 'Importador' in grupos:
return user.rfc.all()
return self.get_queryset_filtrado_por_organizacion()
def perform_create(self, serializer):
@@ -2889,7 +3023,7 @@ def extract_django_suffix(filename):
"""
name_without_ext = os.path.splitext(filename)[0]
match = re.search(r'_([a-zA-Z0-9]{7})$', name_without_ext)
match = re.search(r'_([a-zA-Z0-9]{8})$', name_without_ext)
if match:
return match.group(1)
return None
@@ -2900,10 +3034,10 @@ def get_clean_base_filename(filename):
"""
normalized = normalize_filename(filename)
name_without_ext, ext = os.path.splitext(normalized)
django_suffix = extract_django_suffix(name_without_ext)
if django_suffix:
base_name = name_without_ext[:-8]
base_name = name_without_ext[:-9] # elimina _XXXXXXXX (underscore + 8 chars UUID)
else:
base_name = name_without_ext