diff --git a/api/customs/models.py b/api/customs/models.py index 84fccba..d37612e 100644 --- a/api/customs/models.py +++ b/api/customs/models.py @@ -61,7 +61,7 @@ class Pedimento(models.Model): db_table = 'pedimento' ordering = ['pedimento'] unique_together = [ - ['organizacion', 'pedimento'], + # ['organizacion', 'pedimento'], ['organizacion', 'pedimento_app'] ] diff --git a/api/customs/views_auditor.py b/api/customs/views_auditor.py index 1daf3c0..b46a5be 100644 --- a/api/customs/views_auditor.py +++ b/api/customs/views_auditor.py @@ -667,28 +667,36 @@ def auditar_peticion_respuesta_pedimento_completo(request): pedimento_app = pedimento.pedimento_app tipo_documento_peticion = None tipo_documento_respuesta = None + vista = 'desconocido' if vista_auditar == 'pc': tipo_documento_peticion = 13 tipo_documento_respuesta = 14 + vista = 'Pedimento Completo' elif vista_auditar == 'rm': tipo_documento_peticion = 15 tipo_documento_respuesta = 16 + vista = 'Remesa' elif vista_auditar == 'pt': tipo_documento_peticion = 17 tipo_documento_respuesta = 18 + vista = 'Partidas' elif vista_auditar == 'cove': tipo_documento_peticion = 19 tipo_documento_respuesta = 20 + vista = 'COVEs' elif vista_auditar == 'edoc': tipo_documento_peticion = 21 tipo_documento_respuesta = 22 + vista = 'Edocuments' elif vista_auditar == 'ac_cove': tipo_documento_peticion = 23 tipo_documento_respuesta = 24 + vista = 'Acuses COVEs' elif vista_auditar == 'ac': tipo_documento_peticion = 25 tipo_documento_respuesta = 26 + vista = 'Acuses' if not tipo_documento_peticion and not tipo_documento_respuesta: return Response( @@ -712,7 +720,7 @@ def auditar_peticion_respuesta_pedimento_completo(request): if not documentos_peticion and not documentos_respuesta: return Response( - {'error': 'Registro de documentos de petición y respuesta de partidas no encontrado'}, + {'error': f'Registro de documentos de petición y respuesta de {vista} no encontrado(s)'}, status=status.HTTP_404_NOT_FOUND ) diff --git a/api/record/models.py b/api/record/models.py index 8d7e047..0667460 100644 --- a/api/record/models.py +++ b/api/record/models.py @@ -15,13 +15,21 @@ class Document(models.Model): extension = models.CharField(max_length=60, blank=True, null=True) size = models.PositiveIntegerField() 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) updated_at = models.DateTimeField(auto_now=True) def save(self, *args, **kwargs): 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 uso_almacenamiento, created = UsoAlmacenamiento.objects.get_or_create( organizacion=self.organizacion, diff --git a/api/record/serializers.py b/api/record/serializers.py index cb8738a..933ae40 100644 --- a/api/record/serializers.py +++ b/api/record/serializers.py @@ -13,7 +13,7 @@ class DocumentSerializer(serializers.ModelSerializer): fuente = serializers.PrimaryKeyRelatedField(queryset=Fuente.objects.all()) class Meta: 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') def get_pedimento_numero(self, obj): diff --git a/api/record/views.py b/api/record/views.py index a684f17..33aacef 100644 --- a/api/record/views.py +++ b/api/record/views.py @@ -313,6 +313,85 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin): uso.save() 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') def bulk_delete(self, request): """ @@ -424,10 +503,23 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin): except UsoAlmacenamiento.DoesNotExist: # Si no existe el registro, no hay nada que actualizar pass - - # Eliminar los documentos - deleted_count = existing_documents.count() - existing_documents.delete() + + # Eliminar los documentos (archivos físicos y registros de BD) + archivos_eliminados = 0 + 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: return Response( @@ -437,7 +529,7 @@ class DocumentViewSet(viewsets.ModelViewSet, DocumentosFiltradosMixin): # Agregar errores para IDs no encontrados 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 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 } - if failed_ids: + if errors or failed_ids: response_data.update({ "message": "Algunos documentos no pudieron ser eliminados", "failed_ids": failed_ids,