from rest_framework import serializers from api.customs.models import ( Pedimento, TipoOperacion, ProcesamientoPedimento, EDocument, Cove, Importador, Partida, EstadoDescarga ) from django.db import models from django.db.models import Q from api.record.serializers import DocumentSerializer from api.vucem.serializers import VucemSerializer import logging logger = logging.getLogger(__name__) 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): if not obj: return [] try: # Documentos de respuesta de la partida (tipo 1) vía la FK real # document.partida. 'documentos_vu' lo precarga el ViewSet con prefetch; # si no está, se consulta directo (retrieve u otros callers). docs = getattr(obj, 'documentos_vu', None) if docs is None: docs = list(obj.documents.filter(document_type_id=1).select_related('pedimento', 'fuente')) return DocumentSerializer(docs, many=True, context=self.context).data except Exception as e: logger.warning("get_documentos partida %s: %s", getattr(obj, 'id', '?'), e) 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): """Documentos del e-documento (incluye acuse y errores; excluye solo los REQUEST 21/25) vía la FK real document.edocument. 'documentos_vu' lo precarga el ViewSet con prefetch; si no está, se consulta directo.""" if not obj: return [] try: docs = getattr(obj, 'documentos_vu', None) if docs is None: docs = list(obj.documents.exclude(document_type_id__in=[21, 25]).select_related('pedimento', 'fuente')) return DocumentSerializer(docs, many=True, context=self.context).data except Exception as e: logger.warning("get_documentos edocument %s: %s", getattr(obj, 'id', '?'), e) return [] class Meta: model = EDocument fields = '__all__' read_only_fields = ('created_at', 'updated_at') def validate(self, attrs): # Compatibilidad: payloads legados que solo mandan los booleanos se traducen # al estado de 3 valores (fuente de verdad en el modelo). Un False legado no # degrada un estado 'error' ya asignado. if 'edocument_descargado' in attrs and 'edocument_estado' not in attrs: if attrs['edocument_descargado']: attrs['edocument_estado'] = EstadoDescarga.DESCARGADO elif not (self.instance and self.instance.edocument_estado == EstadoDescarga.ERROR): attrs['edocument_estado'] = EstadoDescarga.PENDIENTE if 'acuse_descargado' in attrs and 'acuse_estado' not in attrs: if attrs['acuse_descargado']: attrs['acuse_estado'] = EstadoDescarga.DESCARGADO elif not (self.instance and self.instance.acuse_estado == EstadoDescarga.ERROR): attrs['acuse_estado'] = EstadoDescarga.PENDIENTE return attrs 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 validate(self, attrs): # Compatibilidad: payloads legados que solo mandan los booleanos se traducen # al estado de 3 valores (fuente de verdad en el modelo). Un False legado no # degrada un estado 'error' ya asignado. if 'cove_descargado' in attrs and 'cove_estado' not in attrs: if attrs['cove_descargado']: attrs['cove_estado'] = EstadoDescarga.DESCARGADO elif not (self.instance and self.instance.cove_estado == EstadoDescarga.ERROR): attrs['cove_estado'] = EstadoDescarga.PENDIENTE if 'acuse_cove_descargado' in attrs and 'acuse_cove_estado' not in attrs: if attrs['acuse_cove_descargado']: attrs['acuse_cove_estado'] = EstadoDescarga.DESCARGADO elif not (self.instance and self.instance.acuse_cove_estado == EstadoDescarga.ERROR): attrs['acuse_cove_estado'] = EstadoDescarga.PENDIENTE return attrs def get_documentos(self, obj): """Documentos del cove (incluye acuse cove y errores; excluye solo los REQUEST 19/23) vía la FK real document.cove. 'documentos_vu' lo precarga el ViewSet con prefetch; si no está, se consulta directo.""" if not obj: return [] try: docs = getattr(obj, 'documentos_vu', None) if docs is None: docs = list(obj.documents.exclude(document_type_id__in=[19, 23]).select_related('pedimento', 'fuente')) return DocumentSerializer(docs, many=True, context=self.context).data except Exception as e: logger.warning("get_documentos cove %s: %s", getattr(obj, 'id', '?'), e) return [] class ImportadorSerializer(serializers.ModelSerializer): class Meta: model = Importador fields = '__all__' read_only_fields = ('created_at', 'updated_at')