from rest_framework import serializers from api.customs.models import ( Pedimento, TipoOperacion, ProcesamientoPedimento, EDocument, Cove, Importador, Partida ) from django.db import models from django.db.models import Q from api.record.models import Document # Asegúrate de importar el modelo Documento from api.record.serializers import DocumentSerializer from api.vucem.serializers import VucemSerializer class PedimentoSerializer(serializers.ModelSerializer): documentos_count = serializers.SerializerMethodField() documentos_peso_total = serializers.SerializerMethodField() def get_documentos_count(self, obj): # Si obj es un dict o no tiene 'documents', devuelve 0 if isinstance(obj, dict) or not hasattr(obj, 'documents'): return 0 return obj.documents.count() def get_documentos_peso_total(self, obj): # Si obj es un dict o no tiene 'documents', devuelve 0 if isinstance(obj, dict) or not hasattr(obj, 'documents'): return 0 return obj.documents.aggregate(total=models.Sum('size'))['total'] or 0 class Meta: model = Pedimento fields = '__all__' read_only_fields = ( 'created_at', 'updated_at', 'organizacion', 'pedimento_app', 'documentos_count', 'documentos_peso_total' ) def to_representation(self, instance): rep = super().to_representation(instance) rep['documentos_count'] = self.get_documentos_count(instance) rep['documentos_peso_total'] = self.get_documentos_peso_total(instance) return rep class PartidaSerializer(serializers.ModelSerializer): documentos = serializers.SerializerMethodField() def get_documentos(self, obj): """ Busca documentos en la tabla `document` que coincidan EXACTAMENTE con: 'documents/vu_PT_{pedimentoApp}_{numero}' al inicio del nombre del archivo. """ if not obj or not getattr(obj, 'pedimento', None): return [] if not obj or not getattr(obj, 'numero_partida', None): return [] try: pedimentoApp = str(obj.pedimento.pedimento_app).strip() numero = str(obj.numero_partida).strip() # Construir el patrón exacto de búsqueda patron_exacto = f'documents/vu_PT_{pedimentoApp}_{numero}.xml' # Buscar documentos que empiecen EXACTAMENTE con ese patrón qs = Document.objects.filter( archivo=patron_exacto ) # Opción 2: Si puede tener diferentes extensiones # patron_base = f'documents/vu_PT_{pedimentoApp}_{numero}' # qs = Document.objects.filter( # archivo__startswith=patron_base # ).filter( # archivo__in=[ # f'{patron_base}.xml', # f'{patron_base}.pdf', # f'{patron_base}.zip' # ] # ) # Filtro adicional por pedimento si el modelo Document tiene este campo if hasattr(Document, 'pedimento'): qs = qs.filter(pedimento=obj.pedimento) # Filtro por organización if hasattr(obj, 'organizacion') and obj.organizacion: qs = qs.filter(organizacion=obj.organizacion) serializer = DocumentSerializer(qs, many=True, context=self.context) return serializer.data #return [] except Exception: # En caso de cualquier error (por ejemplo, importaciones circulares), devolver lista vacía return [] class Meta: model = Partida fields = '__all__' read_only_fields = ('created_at', 'updated_at', 'numero_partida') def validate_unique(self, attrs): """ Sobrescribe la validación de unicidad para manejar correctamente las actualizaciones de registros existentes. """ # Si estamos actualizando un registro existente, excluirlo de la validación if self.instance: # Para actualizaciones, crear una instancia temporal con los nuevos datos # pero sin guardarla, solo para validar unicidad excluyendo el registro actual exclude = {'id': self.instance.id} else: # Para creaciones nuevas, no excluir nada exclude = {} # Crear una instancia temporal con los datos combinados if self.instance: # Combinar datos existentes con los nuevos combined_attrs = {} for field in self.Meta.model._meta.fields: field_name = field.name if field_name in attrs: combined_attrs[field_name] = attrs[field_name] elif hasattr(self.instance, field_name): combined_attrs[field_name] = getattr(self.instance, field_name) else: combined_attrs = attrs # Verificar unique_together manualmente para pedimento + numero_partida if 'pedimento' in combined_attrs and 'numero_partida' in combined_attrs: queryset = self.Meta.model.objects.filter( pedimento=combined_attrs['pedimento'], numero_partida=combined_attrs['numero_partida'] ) # Si estamos actualizando, excluir el registro actual if self.instance: queryset = queryset.exclude(id=self.instance.id) if queryset.exists(): raise serializers.ValidationError({ 'non_field_errors': [ f'Ya existe una partida con el número {combined_attrs["numero_partida"]} para este pedimento.' ] }) def validate(self, data): """ Validación adicional personalizada. """ # Llamar a la validación de unicidad personalizada self.validate_unique(data) return data class TipoOperacionSerializer(serializers.ModelSerializer): class Meta: model = TipoOperacion fields = '__all__' class ProcesamientoPedimentoSerializer(serializers.ModelSerializer): organizacion = serializers.PrimaryKeyRelatedField(queryset=ProcesamientoPedimento._meta.get_field('organizacion').related_model.objects.all(), required=False) organizacion_name = serializers.CharField(source='organizacion.nombre', read_only=True) class Meta: model = ProcesamientoPedimento fields = '__all__' read_only_fields = ('created_at', 'updated_at') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) request = self.context.get('request') # Si no es superusuario, hacer organizacion read_only if request and hasattr(request, 'user') and not request.user.is_superuser: self.fields['organizacion'].read_only = True def to_representation(self, instance): representation = super().to_representation(instance) representation['pedimento'] = PedimentoSerializer(instance.pedimento).data return representation class EDocumentSerializer(serializers.ModelSerializer): documentos = serializers.SerializerMethodField() def get_documentos(self, obj): """ Busca documentos en la tabla `document` que coincidan con el `numero_edocument` dentro del nombre del archivo (`archivo`). Se filtra por organización para evitar devolver documentos de otras orgs. Devuelve la serialización completa de los documentos encontrados: 1. Empiecen con 'vu_EDOCUMENT' en el nombre del archivo 2. Terminen con el numero_edocument + .xml 3. Pertenezcan a la misma organización """ if not obj or not getattr(obj, 'numero_edocument', None): return [] if not obj or not getattr(obj, 'pedimento', None): return [] # if not obj or not getattr(obj, 'pedimento_id', None): # return [] try: numero = str(obj.numero_edocument).strip() # id_pedimento = str(obj.pedimento_id).strip() qs = Document.objects.filter( pedimento=obj.pedimento, archivo__icontains=numero, ) # Filtro por organización si aplica if hasattr(obj, 'organizacion') and obj.organizacion: qs = qs.filter(organizacion=obj.organizacion) serializer = DocumentSerializer(qs, many=True, context=self.context) return serializer.data except Exception: # En caso de cualquier error (por ejemplo, importaciones circulares), devolver lista vacía return [] class Meta: model = EDocument fields = '__all__' read_only_fields = ('created_at', 'updated_at') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Si no es superusuario, hacer organizacion read_only request = self.context.get('request') if request and hasattr(request, 'user') and not request.user.is_superuser: self.fields['organizacion'].read_only = True class CoveSerializer(serializers.ModelSerializer): documentos = serializers.SerializerMethodField() class Meta: model = Cove fields = '__all__' read_only_fields = ('created_at', 'updated_at') def get_documentos(self, obj): """ Busca documentos en la tabla `document` que coincidan con el `numero_cove` dentro del nombre del archivo (`archivo`). Se filtra por organización para evitar devolver documentos de otras orgs. Devuelve la serialización completa de los documentos encontrados: 1. Empiecen con 'vu_COVE' en el nombre del archivo 2. Terminen con el numero_cove + .xml 3. Pertenezcan a la misma organización """ if not obj or not getattr(obj, 'numero_cove', None): return [] if not obj or not getattr(obj, 'pedimento', None): return [] try: numero = str(obj.numero_cove).strip() qs = Document.objects.filter( pedimento=obj.pedimento, archivo__icontains=numero, ) # Filtro por organización si aplica if hasattr(obj, 'organizacion') and obj.organizacion: qs = qs.filter(organizacion=obj.organizacion) serializer = DocumentSerializer(qs, many=True, context=self.context) return serializer.data except Exception: # En caso de cualquier error (por ejemplo, importaciones circulares), devolver lista vacía return [] class ImportadorSerializer(serializers.ModelSerializer): class Meta: model = Importador fields = '__all__' read_only_fields = ('created_at', 'updated_at')