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:
@@ -1278,15 +1278,21 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
|
||||
# Usar tipo de documento por defecto siempre
|
||||
document_type, created = DocumentType.objects.get_or_create(
|
||||
nombre="Documento General",
|
||||
defaults={'descripcion': "Documento general sin tipo específico"}
|
||||
)
|
||||
if created:
|
||||
print(f"✅ DocumentType creado: {document_type.nombre} (ID: {document_type.id})")
|
||||
# Usar tipo de documento indicado o "Documento General" por defecto
|
||||
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:
|
||||
print(f"♻️ DocumentType existente: {document_type.nombre} (ID: {document_type.id})")
|
||||
document_type, _ = DocumentType.objects.get_or_create(
|
||||
nombre="Documento General",
|
||||
defaults={'descripcion': "Documento general sin tipo específico"}
|
||||
)
|
||||
|
||||
uploaded_documents = []
|
||||
failed_files = []
|
||||
@@ -1371,6 +1377,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
existing_doc.archivo = ruta
|
||||
existing_doc.size = file.size
|
||||
existing_doc.extension = extension
|
||||
existing_doc.document_type = document_type
|
||||
existing_doc.save()
|
||||
else:
|
||||
raise Exception(f"Error al guardar archivo: {file.name}")
|
||||
@@ -1406,7 +1413,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
"filename": file.name,
|
||||
"size": file.size,
|
||||
"extension": extension,
|
||||
"document_type": document_type.nombre
|
||||
"document_type": document.document_type.nombre if document.document_type else None
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
@@ -1750,6 +1757,265 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
||||
|
||||
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):
|
||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
serializer_class = DocumentSerializer
|
||||
|
||||
Reference in New Issue
Block a user