feature/agregar eventos en las tareas de fondo, se modificaron modelos para capturar cuales si deben accionar tareas de fondo y cuales no necesariamente tienen que accionar tareas de fondo
This commit is contained in:
@@ -34,6 +34,7 @@ class Pedimento(models.Model):
|
|||||||
fecha_pago = models.DateField(help_text="Fecha de pago del pedimento", blank=True, null=True)
|
fecha_pago = models.DateField(help_text="Fecha de pago del pedimento", blank=True, null=True)
|
||||||
|
|
||||||
alerta = models.BooleanField(default=False, help_text="Indica si el pedimento tiene una alerta asociada")
|
alerta = models.BooleanField(default=False, help_text="Indica si el pedimento tiene una alerta asociada")
|
||||||
|
consultar_vucem = models.BooleanField(default=False, help_text="Solo pedimentos originados desde datastage deben consultar VUCEM automáticamente")
|
||||||
|
|
||||||
contribuyente = models.ForeignKey('Importador', on_delete=models.CASCADE, related_name='pedimentos', help_text="Contribuyente asociado al pedimento", blank=True, null=True)
|
contribuyente = models.ForeignKey('Importador', on_delete=models.CASCADE, related_name='pedimentos', help_text="Contribuyente asociado al pedimento", blank=True, null=True)
|
||||||
agente_aduanal = models.CharField(max_length=100, blank=True, null=True, help_text="RFC del agente aduanal")
|
agente_aduanal = models.CharField(max_length=100, blank=True, null=True, help_text="RFC del agente aduanal")
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ def trigger_celery_task_on_create(sender, instance, created, **kwargs):
|
|||||||
logger.info("NO es creación de pedimento, no se crea procesamiento.")
|
logger.info("NO es creación de pedimento, no se crea procesamiento.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not instance.consultar_vucem:
|
||||||
|
return
|
||||||
|
|
||||||
def crear_procesamiento():
|
def crear_procesamiento():
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('api.customs.async_operations')
|
logger = logging.getLogger('api.customs.async_operations')
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
@@ -615,8 +614,6 @@ def bulk_upload_record_task(self, organizacion_id, parametros, archivo_paths):
|
|||||||
tiene_nomenclatura_especial = True
|
tiene_nomenclatura_especial = True
|
||||||
info_extraida = procesar_archivo_m_con_nomenclatura(file_content, existing_pedimento)
|
info_extraida = procesar_archivo_m_con_nomenclatura(file_content, existing_pedimento)
|
||||||
|
|
||||||
django_file = ContentFile(file_content, name=file_name)
|
|
||||||
|
|
||||||
# Buscar documento existente
|
# Buscar documento existente
|
||||||
existing_documents = Document.objects.filter(
|
existing_documents = Document.objects.filter(
|
||||||
pedimento_id=existing_pedimento.id,
|
pedimento_id=existing_pedimento.id,
|
||||||
@@ -630,51 +627,53 @@ def bulk_upload_record_task(self, organizacion_id, parametros, archivo_paths):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if existing_document:
|
if existing_document:
|
||||||
# Actualizar documento existente
|
|
||||||
# try:
|
|
||||||
# if existing_document.archivo and os.path.exists(existing_document.archivo.path):
|
|
||||||
# os.remove(existing_document.archivo.path)
|
|
||||||
# except (ValueError, OSError):
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# existing_document.archivo = django_file
|
|
||||||
# existing_document.size = len(file_content)
|
|
||||||
# existing_document.extension = extension
|
|
||||||
# existing_document.updated_at = timezone.now()
|
|
||||||
# existing_document.save()
|
|
||||||
|
|
||||||
# doc = Document.objects.get(id=existing_document.id)
|
|
||||||
# doc.archivo.delete(save=False) # Eliminar el archivo anterior
|
|
||||||
# doc.delete() # Eliminar el registro para crear uno nuevo (evita problemas con archivos en Django)
|
|
||||||
|
|
||||||
updated_pedimentos.append({
|
updated_pedimentos.append({
|
||||||
"id": str(existing_pedimento.id),
|
"id": str(existing_pedimento.id),
|
||||||
"pedimento_app": existing_pedimento.pedimento_app,
|
"pedimento_app": existing_pedimento.pedimento_app,
|
||||||
"accion": "Documento actualizado",
|
"accion": "Documento ya existente, omitido",
|
||||||
"documento": file_name
|
"documento": file_name
|
||||||
})
|
})
|
||||||
|
|
||||||
documents_created += 1
|
|
||||||
else:
|
else:
|
||||||
# Crear nuevo documento
|
# Crear registro sin archivo primero
|
||||||
document = Document.objects.create(
|
document = Document.objects.create(
|
||||||
organizacion=organizacion,
|
organizacion=organizacion,
|
||||||
pedimento_id=existing_pedimento.id,
|
pedimento_id=existing_pedimento.id,
|
||||||
document_type=document_type,
|
document_type=document_type,
|
||||||
fuente_id=fuente.id,
|
fuente_id=fuente.id,
|
||||||
archivo=django_file,
|
|
||||||
size=len(file_content),
|
size=len(file_content),
|
||||||
extension=os.path.splitext(file_name)[1].lower().lstrip('.')
|
extension=os.path.splitext(file_name)[1].lower().lstrip('.')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from api.utils.storage_service import storage_service
|
||||||
|
ruta = storage_service.save_document_from_path(
|
||||||
|
file_path=file_path,
|
||||||
|
file_name=file_name,
|
||||||
|
organizacion_id=organizacion.id,
|
||||||
|
pedimento_app=existing_pedimento.pedimento_app,
|
||||||
|
metadata={
|
||||||
|
'pedimento_id': str(existing_pedimento.id),
|
||||||
|
'document_id': str(document.id),
|
||||||
|
'source': 'bulk_upload_async'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if ruta:
|
||||||
|
document.archivo = ruta
|
||||||
|
document.save()
|
||||||
|
documents_created += 1
|
||||||
updated_pedimentos.append({
|
updated_pedimentos.append({
|
||||||
"id": str(existing_pedimento.id),
|
"id": str(existing_pedimento.id),
|
||||||
"pedimento_app": existing_pedimento.pedimento_app,
|
"pedimento_app": existing_pedimento.pedimento_app,
|
||||||
"accion": "Documento creado",
|
"accion": "Documento creado",
|
||||||
"documento": file_name
|
"documento": file_name
|
||||||
})
|
})
|
||||||
|
else:
|
||||||
documents_created += 1
|
document.delete()
|
||||||
|
failed_records.append({
|
||||||
|
"file": relative_path,
|
||||||
|
"archivo_original": folder_name + '.zip',
|
||||||
|
"error": f"Error al guardar {file_name} en almacenamiento"
|
||||||
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed_records.append({
|
failed_records.append({
|
||||||
|
|||||||
@@ -563,11 +563,14 @@ def process_all_organizations():
|
|||||||
"""
|
"""
|
||||||
Envía una tarea por organización activa a la cola org_processing.
|
Envía una tarea por organización activa a la cola org_processing.
|
||||||
"""
|
"""
|
||||||
active_orgs = Organizacion.objects.filter(is_active=True, is_verified=True)
|
active_orgs = Organizacion.objects.filter(
|
||||||
|
is_active=True,
|
||||||
|
is_verified=True,
|
||||||
|
apply_auto_download=True,
|
||||||
|
)
|
||||||
for org in active_orgs:
|
for org in active_orgs:
|
||||||
process_organization_batch.apply_async(
|
process_organization_batch.apply_async(
|
||||||
args=[org.id],
|
args=[str(org.id)],
|
||||||
queue='org_processing'
|
queue='org_processing'
|
||||||
)
|
)
|
||||||
return f"Dispatched {active_orgs.count()} organizations"
|
return f"Dispatched {active_orgs.count()} organizations"
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ def procesar_archivo_asc_task(datastage_id, user_organizacion_id, asc_name):
|
|||||||
"importe_pedimento": data.get('importe_pedimento', 0.0),
|
"importe_pedimento": data.get('importe_pedimento', 0.0),
|
||||||
"existe_expediente": data.get('existe_expediente', False),
|
"existe_expediente": data.get('existe_expediente', False),
|
||||||
"remesas": data.get('remesas', False),
|
"remesas": data.get('remesas', False),
|
||||||
|
"consultar_vucem": True,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
Pedimento.objects.create(**pedimento_data)
|
Pedimento.objects.create(**pedimento_data)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class Organizacion(models.Model):
|
|||||||
|
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
is_verified = models.BooleanField(default=False)
|
is_verified = models.BooleanField(default=False)
|
||||||
|
apply_auto_download = models.BooleanField(default=False)
|
||||||
|
|
||||||
inicio = models.DateField(null=True, blank=True)
|
inicio = models.DateField(null=True, blank=True)
|
||||||
vencimiento = models.DateField(null=True, blank=True)
|
vencimiento = models.DateField(null=True, blank=True)
|
||||||
|
|||||||
@@ -1278,15 +1278,21 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
status=status.HTTP_403_FORBIDDEN
|
status=status.HTTP_403_FORBIDDEN
|
||||||
)
|
)
|
||||||
|
|
||||||
# Usar tipo de documento por defecto siempre
|
# Usar tipo de documento indicado o "Documento General" por defecto
|
||||||
document_type, created = DocumentType.objects.get_or_create(
|
document_type_id_param = request.data.get('document_type_id')
|
||||||
|
if document_type_id_param:
|
||||||
|
try:
|
||||||
|
document_type = DocumentType.objects.get(id=int(document_type_id_param))
|
||||||
|
except (DocumentType.DoesNotExist, ValueError):
|
||||||
|
return Response(
|
||||||
|
{"error": f"Tipo de documento con ID '{document_type_id_param}' no encontrado"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
nombre="Documento General",
|
nombre="Documento General",
|
||||||
defaults={'descripcion': "Documento general sin tipo específico"}
|
defaults={'descripcion': "Documento general sin tipo específico"}
|
||||||
)
|
)
|
||||||
if created:
|
|
||||||
print(f"✅ DocumentType creado: {document_type.nombre} (ID: {document_type.id})")
|
|
||||||
else:
|
|
||||||
print(f"♻️ DocumentType existente: {document_type.nombre} (ID: {document_type.id})")
|
|
||||||
|
|
||||||
uploaded_documents = []
|
uploaded_documents = []
|
||||||
failed_files = []
|
failed_files = []
|
||||||
@@ -1371,6 +1377,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
existing_doc.archivo = ruta
|
existing_doc.archivo = ruta
|
||||||
existing_doc.size = file.size
|
existing_doc.size = file.size
|
||||||
existing_doc.extension = extension
|
existing_doc.extension = extension
|
||||||
|
existing_doc.document_type = document_type
|
||||||
existing_doc.save()
|
existing_doc.save()
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Error al guardar archivo: {file.name}")
|
raise Exception(f"Error al guardar archivo: {file.name}")
|
||||||
@@ -1406,7 +1413,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
"filename": file.name,
|
"filename": file.name,
|
||||||
"size": file.size,
|
"size": file.size,
|
||||||
"extension": extension,
|
"extension": extension,
|
||||||
"document_type": document_type.nombre
|
"document_type": document.document_type.nombre if document.document_type else None
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1750,6 +1757,265 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
|
|
||||||
return Response(response_data, status=response_status)
|
return Response(response_data, status=response_status)
|
||||||
|
|
||||||
|
@action(detail=False, methods=['post'], url_path='create-vu-record', parser_classes=[MultiPartParser])
|
||||||
|
def create_vu_record(self, request):
|
||||||
|
"""
|
||||||
|
Crea un registro (Partida/Cove/EDocument) en su tabla correspondiente
|
||||||
|
y sube sus archivos con la nomenclatura VU.
|
||||||
|
|
||||||
|
FormData:
|
||||||
|
- pedimento_id : UUID del pedimento
|
||||||
|
- tab_seccion : 'partida' | 'cove' | 'edoc'
|
||||||
|
- numero : número del registro a crear
|
||||||
|
- files : archivos (nombre con flag de sección: .xml.general, .pdf.acuse, etc.)
|
||||||
|
"""
|
||||||
|
pedimento_id = request.data.get('pedimento_id')
|
||||||
|
tab_seccion = request.data.get('tab_seccion')
|
||||||
|
numero = request.data.get('numero', '').strip()
|
||||||
|
files = request.FILES.getlist('files')
|
||||||
|
|
||||||
|
if not pedimento_id:
|
||||||
|
return Response({"error": "Se requiere 'pedimento_id'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
if tab_seccion not in ('partida', 'cove', 'edoc'):
|
||||||
|
return Response({"error": "tab_seccion debe ser 'partida', 'cove' o 'edoc'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
if not numero:
|
||||||
|
return Response({"error": "Se requiere 'numero'"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
if not files:
|
||||||
|
return Response({"error": "Se requiere al menos un archivo"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return Response({"error": "Usuario no autenticado"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
from api.customs.models import Pedimento as PedimentoModel, Partida, Cove, EDocument
|
||||||
|
try:
|
||||||
|
pedimento = PedimentoModel.objects.get(id=pedimento_id)
|
||||||
|
except PedimentoModel.DoesNotExist:
|
||||||
|
return Response({"error": "Pedimento no encontrado"}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
organizacion = pedimento.organizacion
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
if not hasattr(request.user, 'organizacion') or request.user.organizacion != organizacion:
|
||||||
|
return Response({"error": "Sin permisos para este pedimento"}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
# Validar número entero para partida
|
||||||
|
numero_int = None
|
||||||
|
if tab_seccion == 'partida':
|
||||||
|
try:
|
||||||
|
numero_int = int(numero)
|
||||||
|
except ValueError:
|
||||||
|
return Response({"error": "El número de partida debe ser un entero"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
uploaded_documents = []
|
||||||
|
failed_files = []
|
||||||
|
errors = []
|
||||||
|
total_space_used = 0
|
||||||
|
expediente_obj = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with transaction.atomic():
|
||||||
|
uso = UsoAlmacenamiento.objects.select_for_update().get_or_create(
|
||||||
|
organizacion=organizacion,
|
||||||
|
defaults={'espacio_utilizado': 0}
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
max_bytes = organizacion.licencia.almacenamiento * 1024 ** 3
|
||||||
|
total_files_size = sum(f.size for f in files)
|
||||||
|
if uso.espacio_utilizado + total_files_size > max_bytes:
|
||||||
|
espacio_faltante = (uso.espacio_utilizado + total_files_size) - max_bytes
|
||||||
|
return Response({
|
||||||
|
"error": "Espacio de almacenamiento insuficiente",
|
||||||
|
"espacio_faltante_gb": round(espacio_faltante / (1024 ** 3), 2),
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# Verificar unicidad y crear registro
|
||||||
|
if tab_seccion == 'partida':
|
||||||
|
if Partida.objects.filter(pedimento=pedimento, numero_partida=numero_int).exists():
|
||||||
|
return Response(
|
||||||
|
{"error": f"La partida {numero} ya existe para este pedimento"},
|
||||||
|
status=status.HTTP_409_CONFLICT
|
||||||
|
)
|
||||||
|
expediente_obj = Partida.objects.create(
|
||||||
|
pedimento=pedimento,
|
||||||
|
organizacion=organizacion,
|
||||||
|
numero_partida=numero_int
|
||||||
|
)
|
||||||
|
elif tab_seccion == 'cove':
|
||||||
|
if Cove.objects.filter(pedimento=pedimento, numero_cove=numero).exists():
|
||||||
|
return Response(
|
||||||
|
{"error": f"El COVE {numero} ya existe para este pedimento"},
|
||||||
|
status=status.HTTP_409_CONFLICT
|
||||||
|
)
|
||||||
|
expediente_obj = Cove.objects.create(
|
||||||
|
pedimento=pedimento,
|
||||||
|
organizacion=organizacion,
|
||||||
|
numero_cove=numero
|
||||||
|
)
|
||||||
|
elif tab_seccion == 'edoc':
|
||||||
|
if EDocument.objects.filter(pedimento=pedimento, numero_edocument=numero).exists():
|
||||||
|
return Response(
|
||||||
|
{"error": f"El EDocument {numero} ya existe para este pedimento"},
|
||||||
|
status=status.HTTP_409_CONFLICT
|
||||||
|
)
|
||||||
|
expediente_obj = EDocument.objects.create(
|
||||||
|
pedimento=pedimento,
|
||||||
|
organizacion=organizacion,
|
||||||
|
numero_edocument=numero
|
||||||
|
)
|
||||||
|
|
||||||
|
espacio_usado_temp = uso.espacio_utilizado
|
||||||
|
uploaded_secciones = set()
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
try:
|
||||||
|
if not file.name:
|
||||||
|
failed_files.append("archivo_sin_nombre")
|
||||||
|
errors.append("Archivo sin nombre detectado")
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename = file.name
|
||||||
|
if '.' in filename:
|
||||||
|
base = '.'.join(filename.split('.')[:-1])
|
||||||
|
secciones = filename.split('.')[-1]
|
||||||
|
else:
|
||||||
|
base = filename
|
||||||
|
secciones = ''
|
||||||
|
|
||||||
|
file.name = base
|
||||||
|
extension = file.name.split('.')[-1].lower() if '.' in file.name else ''
|
||||||
|
|
||||||
|
if tab_seccion == 'partida':
|
||||||
|
nuevo_nombre = f"vu_PT_{pedimento.pedimento_app}_{numero}.{extension}"
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
|
nombre="Pedimento Partida",
|
||||||
|
defaults={'descripcion': "Tag para saber que el archivo guarda una partida"}
|
||||||
|
)
|
||||||
|
elif tab_seccion == 'cove':
|
||||||
|
if secciones == 'acuse':
|
||||||
|
nuevo_nombre = f"vu_AC_COVE_{pedimento.pedimento_app}_{numero}.{extension}"
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
|
nombre="Acuse Cove",
|
||||||
|
defaults={'descripcion': "Tag para saber que el archivo guarda un acuse de cove"}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
nuevo_nombre = f"vu_COVE_{pedimento.pedimento_app}_{numero}.{extension}"
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
|
nombre="Cove",
|
||||||
|
defaults={'descripcion': "Tag para saber que el archivo guarda un cove"}
|
||||||
|
)
|
||||||
|
elif tab_seccion == 'edoc':
|
||||||
|
if secciones == 'acuse':
|
||||||
|
nuevo_nombre = f"vu_AC_{pedimento.pedimento_app}_{numero}.{extension}"
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
|
nombre="Pedimento Acuse",
|
||||||
|
defaults={'descripcion': "Tag para saber que el documento es un Acuse"}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
nuevo_nombre = f"vu_ED_{pedimento.pedimento_app}_{numero}.{extension}"
|
||||||
|
document_type, _ = DocumentType.objects.get_or_create(
|
||||||
|
nombre="Pedimento EDocument",
|
||||||
|
defaults={'descripcion': "Tag para saber que el documento es un EDocument"}
|
||||||
|
)
|
||||||
|
|
||||||
|
file.name = nuevo_nombre
|
||||||
|
|
||||||
|
document = Document.objects.create(
|
||||||
|
organizacion=organizacion,
|
||||||
|
pedimento_id=pedimento_id,
|
||||||
|
document_type=document_type,
|
||||||
|
size=file.size,
|
||||||
|
extension=extension
|
||||||
|
)
|
||||||
|
|
||||||
|
ruta = storage_service.save_document(
|
||||||
|
file=file,
|
||||||
|
organizacion_id=organizacion.id,
|
||||||
|
pedimento_app=pedimento.pedimento_app,
|
||||||
|
metadata={'source': 'create_vu_record'}
|
||||||
|
)
|
||||||
|
|
||||||
|
if ruta:
|
||||||
|
document.archivo = ruta
|
||||||
|
document.save()
|
||||||
|
else:
|
||||||
|
document.delete()
|
||||||
|
raise Exception(f"Error al guardar archivo: {file.name}")
|
||||||
|
|
||||||
|
espacio_usado_temp += file.size
|
||||||
|
total_space_used += file.size
|
||||||
|
uploaded_secciones.add(secciones)
|
||||||
|
|
||||||
|
uploaded_documents.append({
|
||||||
|
"id": str(document.id),
|
||||||
|
"filename": file.name,
|
||||||
|
"size": file.size,
|
||||||
|
"extension": extension,
|
||||||
|
"document_type": document_type.nombre
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
failed_files.append(file.name)
|
||||||
|
errors.append(f"Error al procesar {file.name}: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Actualizar flags de descarga según secciones subidas exitosamente
|
||||||
|
if tab_seccion == 'partida':
|
||||||
|
if uploaded_secciones:
|
||||||
|
expediente_obj.descargado = True
|
||||||
|
expediente_obj.save(update_fields=['descargado'])
|
||||||
|
elif tab_seccion == 'cove':
|
||||||
|
update_fields = []
|
||||||
|
if 'general' in uploaded_secciones:
|
||||||
|
expediente_obj.cove_descargado = True
|
||||||
|
update_fields.append('cove_descargado')
|
||||||
|
if 'acuse' in uploaded_secciones:
|
||||||
|
expediente_obj.acuse_cove_descargado = True
|
||||||
|
update_fields.append('acuse_cove_descargado')
|
||||||
|
if update_fields:
|
||||||
|
expediente_obj.save(update_fields=update_fields)
|
||||||
|
elif tab_seccion == 'edoc':
|
||||||
|
update_fields = []
|
||||||
|
if 'general' in uploaded_secciones:
|
||||||
|
expediente_obj.edocument_descargado = True
|
||||||
|
update_fields.append('edocument_descargado')
|
||||||
|
if 'acuse' in uploaded_secciones:
|
||||||
|
expediente_obj.acuse_descargado = True
|
||||||
|
update_fields.append('acuse_descargado')
|
||||||
|
if update_fields:
|
||||||
|
expediente_obj.save(update_fields=update_fields)
|
||||||
|
|
||||||
|
uso.espacio_utilizado = espacio_usado_temp
|
||||||
|
uso.save()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return Response(
|
||||||
|
{"error": f"Error durante el procesamiento: {str(e)}"},
|
||||||
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
space_used_mb = round(total_space_used / (1024 * 1024), 2)
|
||||||
|
response_data = {
|
||||||
|
"uploaded_count": len(uploaded_documents),
|
||||||
|
"uploaded_documents": uploaded_documents,
|
||||||
|
"space_used_mb": space_used_mb,
|
||||||
|
"pedimento_id": str(pedimento_id),
|
||||||
|
"expediente_id": str(expediente_obj.id),
|
||||||
|
"tab_seccion": tab_seccion,
|
||||||
|
"numero": numero,
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed_files:
|
||||||
|
response_data.update({
|
||||||
|
"message": f"Registro creado pero algunos archivos fallaron",
|
||||||
|
"failed_files": failed_files,
|
||||||
|
"errors": errors
|
||||||
|
})
|
||||||
|
response_status = status.HTTP_207_MULTI_STATUS
|
||||||
|
else:
|
||||||
|
response_data["message"] = f"{tab_seccion.capitalize()} {numero} creado exitosamente"
|
||||||
|
response_status = status.HTTP_201_CREATED
|
||||||
|
|
||||||
|
return Response(response_data, status=response_status)
|
||||||
|
|
||||||
|
|
||||||
class ProtectedDocumentDownloadView(APIView, DocumentosFiltradosMixin):
|
class ProtectedDocumentDownloadView(APIView, DocumentosFiltradosMixin):
|
||||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||||
serializer_class = DocumentSerializer
|
serializer_class = DocumentSerializer
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import tempfile
|
|||||||
from api.utils.storage_service import storage_service
|
from api.utils.storage_service import storage_service
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from api.organization.models import Organizacion
|
from api.organization.models import Organizacion
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from api.reports.models import ReportDocument
|
from api.reports.models import ReportDocument
|
||||||
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
||||||
@@ -127,8 +126,8 @@ def generate_report_document(report_id):
|
|||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def generate_report_control_pedimento(report_id):
|
def generate_report_control_pedimento(report_id):
|
||||||
|
report = None
|
||||||
try:
|
try:
|
||||||
|
|
||||||
report = ReportDocument.objects.get(id=report_id)
|
report = ReportDocument.objects.get(id=report_id)
|
||||||
report.status = 'processing'
|
report.status = 'processing'
|
||||||
report.save(update_fields=['status'])
|
report.save(update_fields=['status'])
|
||||||
@@ -222,8 +221,9 @@ def generate_report_control_pedimento(report_id):
|
|||||||
|
|
||||||
# 4. GENERAR CSV CON DETALLES
|
# 4. GENERAR CSV CON DETALLES
|
||||||
filename = f"report_{report.id}_{timezone.now().strftime('%Y%m%d%H%M%S')}.csv"
|
filename = f"report_{report.id}_{timezone.now().strftime('%Y%m%d%H%M%S')}.csv"
|
||||||
file_path = os.path.join(settings.MEDIA_ROOT, 'reports', filename)
|
|
||||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', encoding='utf-8', newline='') as tmp:
|
||||||
|
tmp_path = tmp.name
|
||||||
|
|
||||||
todas_las_filas = []
|
todas_las_filas = []
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ def generate_report_control_pedimento(report_id):
|
|||||||
todas_las_filas.append(fila)
|
todas_las_filas.append(fila)
|
||||||
|
|
||||||
# 5. ESCRIBIR ARCHIVO CSV
|
# 5. ESCRIBIR ARCHIVO CSV
|
||||||
with open(file_path, 'w', newline='', encoding='utf-8') as f:
|
with open(tmp_path, 'w', newline='', encoding='utf-8') as f:
|
||||||
writer = csv.writer(f)
|
writer = csv.writer(f)
|
||||||
|
|
||||||
# SECCIÓN DE TOTALES
|
# SECCIÓN DE TOTALES
|
||||||
@@ -308,14 +308,39 @@ def generate_report_control_pedimento(report_id):
|
|||||||
writer.writerow(fila)
|
writer.writerow(fila)
|
||||||
|
|
||||||
|
|
||||||
with open(file_path, 'rb') as f:
|
with open(tmp_path, 'rb') as f:
|
||||||
report.file.save(filename, ContentFile(f.read()), save=True)
|
file_content = f.read()
|
||||||
|
|
||||||
|
uploaded_file = SimpleUploadedFile(
|
||||||
|
name=filename,
|
||||||
|
content=file_content,
|
||||||
|
content_type='text/csv'
|
||||||
|
)
|
||||||
|
|
||||||
|
ruta = storage_service.save_report(
|
||||||
|
file=uploaded_file,
|
||||||
|
organizacion_id=filters.get('organizacion_id'),
|
||||||
|
metadata={
|
||||||
|
'report_id': str(report.id),
|
||||||
|
'report_type': 'control_pedimento',
|
||||||
|
'user_id': str(report.user.id) if report.user else None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
|
||||||
|
if ruta:
|
||||||
|
report.file = ruta
|
||||||
report.status = 'ready'
|
report.status = 'ready'
|
||||||
|
else:
|
||||||
|
report.status = 'error'
|
||||||
|
report.error_message = 'Error al guardar el archivo en storage'
|
||||||
|
|
||||||
report.finished_at = timezone.now()
|
report.finished_at = timezone.now()
|
||||||
report.save(update_fields=['status', 'file', 'finished_at'])
|
report.save(update_fields=['status', 'file', 'finished_at', 'error_message'])
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if report:
|
||||||
report.status = 'error'
|
report.status = 'error'
|
||||||
report.error_message = str(e)
|
report.error_message = str(e)
|
||||||
report.finished_at = timezone.now()
|
report.finished_at = timezone.now()
|
||||||
|
|||||||
Reference in New Issue
Block a user