from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.pagination import PageNumberPagination from django.db.models import Value, CharField, Q, Exists, OuterRef, Subquery from django.db.models.functions import Cast from datetime import datetime, timedelta from api.customs.models import Pedimento, Cove, EDocument, Partida class CustomPagination(PageNumberPagination): page_size = 50 page_size_query_param = 'page_size' max_page_size = 1000 @api_view(['GET']) @permission_classes([IsAuthenticated]) def table_summary(request): """ Endpoint que devuelve un resumen tabulado de pedimentos y sus documentos asociados. """ org_id = request.query_params.get('organizacion_id') if not org_id: return Response({"error": "organizacion_id es requerido"}, status=400) # Obtener filtros de query params tipo_documento = request.query_params.get('tipo_documento') rfc = request.query_params.get('contribuyente__rfc') fecha_pago_gte = request.query_params.get('fecha_pago__gte') fecha_pago_lte = request.query_params.get('fecha_pago__lte') patente = request.query_params.get('patente') aduana = request.query_params.get('aduana') pedimento = request.query_params.get('pedimento') pedimento_app = request.query_params.get('pedimento_app') regimen = request.query_params.get('regimen') tipo_operacion = request.query_params.get('tipo_operacion') # Si no se proporcionan fechas, establecer un rango por defecto de los últimos 30 días if not fecha_pago_gte and not fecha_pago_lte: fecha_pago_lte = datetime.now().date() fecha_pago_gte = fecha_pago_lte - timedelta(days=30) # Construir filtros base para pedimentos pedimentos_filters = Q(organizacion_id=org_id) # Añadir filtros de fecha siempre para limitar el conjunto de datos pedimentos_filters &= Q(fecha_pago__gte=fecha_pago_gte) pedimentos_filters &= Q(fecha_pago__lte=fecha_pago_lte) if rfc: pedimentos_filters &= Q(contribuyente__rfc=rfc) if patente: pedimentos_filters &= Q(patente=patente) if aduana: pedimentos_filters &= Q(aduana=aduana) if pedimento: pedimentos_filters &= Q(pedimento=pedimento) if pedimento_app: pedimentos_filters &= Q(pedimento_app=pedimento_app) if regimen: pedimentos_filters &= Q(regimen=regimen) if tipo_operacion: pedimentos_filters &= Q(tipo_operacion_id=tipo_operacion) # Query base desde pedimentos con todas las subconsultas necesarias resultado = Pedimento.objects.filter(pedimentos_filters).values( 'aduana', 'patente', 'regimen', 'pedimento', 'pedimento_app', 'clave_pedimento', 'tipo_operacion_id', 'contribuyente_id' ) # Generar queries según el tipo de documento solicitado queries = [] if not tipo_documento or tipo_documento == 'ACUSE COVE': coves_acuse = resultado.annotate( identificador=Cast(Subquery( Cove.objects.filter(pedimento_id=OuterRef('id')).values('numero_cove')[:1] ), CharField()), documento=Value('ACUSE COVE', CharField()), estado=Cast(Subquery( Cove.objects.filter(pedimento_id=OuterRef('id')).values('acuse_cove_descargado')[:1] ), CharField()) ).filter(identificador__isnull=False) queries.append(coves_acuse) if not tipo_documento or tipo_documento == 'COVE': coves = resultado.annotate( identificador=Cast(Subquery( Cove.objects.filter(pedimento_id=OuterRef('id')).values('numero_cove')[:1] ), CharField()), documento=Value('COVE', CharField()), estado=Cast(Subquery( Cove.objects.filter(pedimento_id=OuterRef('id')).values('cove_descargado')[:1] ), CharField()) ).filter(identificador__isnull=False) queries.append(coves) if not tipo_documento or tipo_documento == 'ACUSE EDOC': edocs_acuse = resultado.annotate( identificador=Cast(Subquery( EDocument.objects.filter(pedimento_id=OuterRef('id')).values('numero_edocument')[:1] ), CharField()), documento=Value('ACUSE EDOC', CharField()), estado=Cast(Subquery( EDocument.objects.filter(pedimento_id=OuterRef('id')).values('acuse_descargado')[:1] ), CharField()) ).filter(identificador__isnull=False) queries.append(edocs_acuse) if not tipo_documento or tipo_documento == 'EDOC': edocs = resultado.annotate( identificador=Cast(Subquery( EDocument.objects.filter(pedimento_id=OuterRef('id')).values('numero_edocument')[:1] ), CharField()), documento=Value('EDOC', CharField()), estado=Cast(Subquery( EDocument.objects.filter(pedimento_id=OuterRef('id')).values('edocument_descargado')[:1] ), CharField()) ).filter(identificador__isnull=False) queries.append(edocs) if not tipo_documento or tipo_documento == 'PARTIDA': partidas = resultado.annotate( identificador=Cast(Subquery( Partida.objects.filter(pedimento_id=OuterRef('id')).values('numero_partida')[:1] ), CharField()), documento=Value('PARTIDA', CharField()), estado=Cast(Subquery( Partida.objects.filter(pedimento_id=OuterRef('id')).values('descargado')[:1] ), CharField()) ).filter(identificador__isnull=False) queries.append(partidas) # Unir los resultados usando UNION ALL para mejor rendimiento if not queries: return Response([]) resultado_final = queries[0] for query in queries[1:]: resultado_final = resultado_final.union(query, all=True) # Aplicar paginación paginator = CustomPagination() page = paginator.paginate_queryset( resultado_final.order_by('pedimento', 'documento'), request ) return paginator.get_paginated_response({ "results": page, "filtros_aplicados": { "organizacion_id": org_id, "tipo_documento": tipo_documento, "contribuyente__rfc": rfc, "fecha_pago__gte": fecha_pago_gte, "fecha_pago__lte": fecha_pago_lte, "patente": patente, "aduana": aduana, "pedimento": pedimento, "pedimento_app": pedimento_app, "regimen": regimen, "tipo_operacion": tipo_operacion } })