fix: se crea una nueva pestaña en detalle de expediente para visualizar los archivos de errores devueltos por ventanilla unica.
This commit is contained in:
@@ -61,7 +61,7 @@ class Pedimento(models.Model):
|
|||||||
db_table = 'pedimento'
|
db_table = 'pedimento'
|
||||||
ordering = ['pedimento']
|
ordering = ['pedimento']
|
||||||
unique_together = [
|
unique_together = [
|
||||||
['organizacion', 'pedimento'],
|
# ['organizacion', 'pedimento'],
|
||||||
['organizacion', 'pedimento_app']
|
['organizacion', 'pedimento_app']
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Document(models.Model):
|
|||||||
extension = models.CharField(max_length=60, blank=True, null=True)
|
extension = models.CharField(max_length=60, blank=True, null=True)
|
||||||
size = models.PositiveIntegerField()
|
size = models.PositiveIntegerField()
|
||||||
fuente = models.ForeignKey('Fuente', on_delete=models.CASCADE, related_name='documents', blank=True, null=True)
|
fuente = models.ForeignKey('Fuente', on_delete=models.CASCADE, related_name='documents', blank=True, null=True)
|
||||||
|
vu = models.BooleanField(default=False)
|
||||||
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
@@ -22,6 +23,13 @@ class Document(models.Model):
|
|||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
is_new = self._state.adding
|
is_new = self._state.adding
|
||||||
|
|
||||||
|
# Calcular automáticamente el campo vu
|
||||||
|
if self.document_type_id:
|
||||||
|
# rango de IDs que indican documentos VU
|
||||||
|
self.vu = 13 <= self.document_type_id <= 26
|
||||||
|
else:
|
||||||
|
self.vu = False
|
||||||
|
|
||||||
# Usar get_or_create en lugar de get para manejar el caso cuando no existe
|
# Usar get_or_create en lugar de get para manejar el caso cuando no existe
|
||||||
uso_almacenamiento, created = UsoAlmacenamiento.objects.get_or_create(
|
uso_almacenamiento, created = UsoAlmacenamiento.objects.get_or_create(
|
||||||
organizacion=self.organizacion,
|
organizacion=self.organizacion,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class DocumentSerializer(serializers.ModelSerializer):
|
|||||||
fuente = serializers.PrimaryKeyRelatedField(queryset=Fuente.objects.all())
|
fuente = serializers.PrimaryKeyRelatedField(queryset=Fuente.objects.all())
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Document
|
model = Document
|
||||||
fields = ('id', 'organizacion', 'pedimento', 'pedimento_numero', 'archivo', 'document_type', 'size', 'extension', 'fuente','fuente_nombre','created_at', 'updated_at')
|
fields = ('id', 'organizacion', 'pedimento', 'pedimento_numero', 'archivo', 'document_type', 'size', 'extension', 'fuente','fuente_nombre','created_at', 'updated_at','vu')
|
||||||
read_only_fields = ('id', 'size', 'extension', 'created_at', 'updated_at', 'pedimento_numero')
|
read_only_fields = ('id', 'size', 'extension', 'created_at', 'updated_at', 'pedimento_numero')
|
||||||
|
|
||||||
def get_pedimento_numero(self, obj):
|
def get_pedimento_numero(self, obj):
|
||||||
|
|||||||
@@ -313,6 +313,85 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
uso.save()
|
uso.save()
|
||||||
instance.delete()
|
instance.delete()
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'], url_path='vu-documentos-errores')
|
||||||
|
def vu_documentos_errores(self, request):
|
||||||
|
"""
|
||||||
|
Endpoint para obtener los documentos VU de error obtenidoss.
|
||||||
|
Filtra documentos cuyo document_type está en el rango de IDs de documentos VU (13-26).
|
||||||
|
"""
|
||||||
|
queryset = self.get_queryset().filter(vu=True)
|
||||||
|
|
||||||
|
pedimento_id = request.query_params.get('pedimentoId')
|
||||||
|
filtroExtension = request.query_params.get('extension')
|
||||||
|
filtroArchivo = request.query_params.get('archivo__icontains')
|
||||||
|
filtroFechaCreacion = request.query_params.get('created_at__date')
|
||||||
|
filtroTipoError = request.query_params.get('tipo_error')
|
||||||
|
filtroFuente = request.query_params.get('fuente')
|
||||||
|
document_type_ids = request.query_params.get('document_type_id')
|
||||||
|
|
||||||
|
if pedimento_id:
|
||||||
|
try:
|
||||||
|
pedimento_obj = Pedimento.objects.get(id=pedimento_id)
|
||||||
|
queryset = queryset.filter(pedimento_id=pedimento_id)
|
||||||
|
except Pedimento.DoesNotExist:
|
||||||
|
return Response(
|
||||||
|
{"error": "No se encontró el pedimento especificado"},
|
||||||
|
status=status.HTTP_404_NOT_FOUND
|
||||||
|
)
|
||||||
|
|
||||||
|
if filtroArchivo:
|
||||||
|
try:
|
||||||
|
queryset = queryset.filter(archivo__icontains=filtroArchivo)
|
||||||
|
except ValueError:
|
||||||
|
return Response(
|
||||||
|
{"error": "El parámetro Archivo debe ser caracteres válidos"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
if filtroExtension:
|
||||||
|
try:
|
||||||
|
queryset = queryset.filter(extension__iexact=filtroExtension)
|
||||||
|
except ValueError:
|
||||||
|
return Response(
|
||||||
|
{"error": "El parámetro extension debe ser una extensión válida"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
if filtroFechaCreacion:
|
||||||
|
from django.utils.dateparse import parse_date
|
||||||
|
|
||||||
|
fecha = parse_date(filtroFechaCreacion)
|
||||||
|
if not fecha:
|
||||||
|
return Response(
|
||||||
|
{"error": "El parámetro created_at__date debe tener el formato YYYY-MM-DD"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
queryset = queryset.filter(created_at__date=fecha)
|
||||||
|
|
||||||
|
if filtroTipoError:
|
||||||
|
try:
|
||||||
|
ids = [int(i) for i in filtroTipoError.split(',')]
|
||||||
|
queryset = queryset.filter(document_type_id__in=ids)
|
||||||
|
except ValueError:
|
||||||
|
return Response(
|
||||||
|
{"error": "El parámetro document_type_id debe ser una lista de IDs separados por comas"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
if filtroFuente:
|
||||||
|
try:
|
||||||
|
ids = [int(i) for i in filtroFuente.split(',')]
|
||||||
|
queryset = queryset.filter(fuente_id__in=ids)
|
||||||
|
except ValueError:
|
||||||
|
return Response(
|
||||||
|
{"error": "El parámetro fuente debe ser una lista de IDs separados por comas"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=False, methods=['post'], url_path='bulk-delete')
|
@action(detail=False, methods=['post'], url_path='bulk-delete')
|
||||||
def bulk_delete(self, request):
|
def bulk_delete(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -425,9 +504,22 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
# Si no existe el registro, no hay nada que actualizar
|
# Si no existe el registro, no hay nada que actualizar
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Eliminar los documentos
|
# Eliminar los documentos (archivos físicos y registros de BD)
|
||||||
deleted_count = existing_documents.count()
|
archivos_eliminados = 0
|
||||||
existing_documents.delete()
|
for doc in existing_documents:
|
||||||
|
try:
|
||||||
|
# Eliminar archivo físico
|
||||||
|
if doc.archivo and doc.archivo.storage.exists(doc.archivo.name):
|
||||||
|
doc.archivo.delete(save=False) # save=False para no intentar guardar el modelo
|
||||||
|
|
||||||
|
# Eliminar registro de la base de datos
|
||||||
|
doc.delete()
|
||||||
|
archivos_eliminados += 1
|
||||||
|
except Exception as e:
|
||||||
|
errors.append(f"No se pudo eliminar el documento {doc.id}: {str(e)}")
|
||||||
|
failed_ids.append(str(doc.id))
|
||||||
|
|
||||||
|
deleted_count = archivos_eliminados
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Response(
|
return Response(
|
||||||
@@ -437,7 +529,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
|
|
||||||
# Agregar errores para IDs no encontrados
|
# Agregar errores para IDs no encontrados
|
||||||
if failed_ids:
|
if failed_ids:
|
||||||
errors = [f"No se encontró el documento con ID {id} o no pertenece a su organización" for id in failed_ids]
|
errors.extend([f"No se encontró el documento con ID {id} o no pertenece a su organización" for id in failed_ids])
|
||||||
|
|
||||||
# Convertir bytes a MB para la respuesta
|
# Convertir bytes a MB para la respuesta
|
||||||
space_freed_mb = round(total_space_freed / (1024 * 1024), 2)
|
space_freed_mb = round(total_space_freed / (1024 * 1024), 2)
|
||||||
@@ -449,7 +541,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin):
|
|||||||
"space_freed_mb": space_freed_mb
|
"space_freed_mb": space_freed_mb
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed_ids:
|
if errors or failed_ids:
|
||||||
response_data.update({
|
response_data.update({
|
||||||
"message": "Algunos documentos no pudieron ser eliminados",
|
"message": "Algunos documentos no pudieron ser eliminados",
|
||||||
"failed_ids": failed_ids,
|
"failed_ids": failed_ids,
|
||||||
|
|||||||
Reference in New Issue
Block a user