actualizacion de reportes
This commit is contained in:
@@ -48,7 +48,10 @@ from core.permissions import (
|
||||
IsSuperUser
|
||||
)
|
||||
from .serializers import ExportModelSerializer
|
||||
|
||||
import uuid
|
||||
import datetime
|
||||
import zipfile
|
||||
from django.db import models
|
||||
|
||||
def export_model_to_csv(request, model_name, fields, module='datastage', filters=None):
|
||||
model = apps.get_model(module, model_name)
|
||||
@@ -86,11 +89,688 @@ def export_model_to_excel(request, model_name, fields, module='datastage', filte
|
||||
response['Content-Disposition'] = f'attachment; filename="{model_name}.xlsx"'
|
||||
return response
|
||||
|
||||
# class ControlPedimentoView(APIView):
|
||||
# my_tags = ['Control-Pedimento']
|
||||
# permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
# @swagger_auto_schema(request_body=ExportModelSerializer, responses={200: 'Archivo generado (Excel o CSV)'})
|
||||
# def post(self, request, *args, **kwargs):
|
||||
# """
|
||||
# Endpoint específico para exportación de DataStage con soporte múltiple
|
||||
# """
|
||||
# # Verificar si es modo múltiple
|
||||
# modo = request.data.get('modo', 'simple')
|
||||
|
||||
# if modo == 'multiple':
|
||||
# return self.handle_multiple_export(request)
|
||||
# else:
|
||||
# return self.handle_simple_export(request)
|
||||
|
||||
|
||||
|
||||
class ExportDataStageView(APIView):
|
||||
my_tags = ['Reportes-DataStage']
|
||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
# Constantes para partición
|
||||
# MAX_RECORDS_PER_FILE = 100 # Límite seguro por archivo
|
||||
MAX_RECORDS_PER_FILE = 50000 # Límite seguro por archivo
|
||||
|
||||
def safe_excel_value(self, value):
|
||||
"""
|
||||
Convierte cualquier valor a un formato seguro para Excel
|
||||
"""
|
||||
if value is None:
|
||||
return ''
|
||||
elif isinstance(value, (uuid.UUID,)):
|
||||
return str(value)
|
||||
elif hasattr(value, 'uuid'):
|
||||
return str(value.uuid)
|
||||
elif hasattr(value, 'id'):
|
||||
return str(value.id)
|
||||
elif isinstance(value, (datetime.datetime, datetime.date)):
|
||||
return value.isoformat()
|
||||
elif isinstance(value, (dict, list)):
|
||||
return str(value)
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
@swagger_auto_schema(request_body=ExportModelSerializer, responses={200: 'Archivo generado (Excel o CSV)'})
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Endpoint específico para exportación de DataStage con soporte múltiple
|
||||
"""
|
||||
# Verificar si es modo múltiple
|
||||
modo = request.data.get('modo', 'simple')
|
||||
|
||||
if modo == 'multiple':
|
||||
return self.handle_multiple_export(request)
|
||||
else:
|
||||
return self.handle_simple_export(request)
|
||||
|
||||
def handle_simple_export(self, request):
|
||||
"""Maneja exportación simple de DataStage (un solo modelo)"""
|
||||
model_name = request.data.get('model')
|
||||
fields = request.data.get('fields')
|
||||
global_filters = request.data.get('globalFilters', {})
|
||||
export_type = request.data.get('format', 'csv')
|
||||
module = 'datastage'
|
||||
|
||||
if not model_name or not fields:
|
||||
return Response({'error': 'model and fields are required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
model = apps.get_model(module, model_name)
|
||||
filters = self.apply_global_filters_to_model(global_filters, model, request.user)
|
||||
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
total_records = queryset.count()
|
||||
|
||||
if export_type == 'excel':
|
||||
# Verificar si necesita partición
|
||||
if total_records > self.MAX_RECORDS_PER_FILE:
|
||||
return self.export_single_model_partitioned(request, model_name, fields, filters, total_records)
|
||||
else:
|
||||
return export_model_to_excel(request, model_name, fields, module, filters)
|
||||
else:
|
||||
if total_records > self.MAX_RECORDS_PER_FILE:
|
||||
return self.export_single_model_csv_partitioned(request, model_name, fields, filters, total_records)
|
||||
else:
|
||||
return export_model_to_csv(request, model_name, fields, module, filters)
|
||||
|
||||
except LookupError:
|
||||
return Response({'error': f'Model {model_name} not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def handle_multiple_export(self, request):
|
||||
"""Maneja exportación múltiple de DataStage (varios modelos)"""
|
||||
models_data = request.data.get('models', [])
|
||||
export_type = request.data.get('format', 'csv')
|
||||
global_filters = request.data.get('globalFilters', {})
|
||||
|
||||
if not models_data:
|
||||
return Response({'error': 'models are required for multiple export'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
related_keys = self.get_related_keys_from_filters(global_filters, models_data, request.user)
|
||||
total_estimated_records = self.estimate_total_records(models_data, global_filters, related_keys, request.user)
|
||||
|
||||
if total_estimated_records > self.MAX_RECORDS_PER_FILE:
|
||||
if export_type == 'excel':
|
||||
return self.export_datastage_multiple_partitioned_excel(request, models_data, global_filters, related_keys)
|
||||
else:
|
||||
return self.export_datastage_multiple_partitioned_csv(request, models_data, global_filters, related_keys)
|
||||
else:
|
||||
if export_type == 'excel':
|
||||
return self.export_datastage_multiple_to_excel(request, models_data, global_filters, related_keys)
|
||||
else:
|
||||
return self.export_datastage_multiple_to_csv(request, models_data, global_filters, related_keys)
|
||||
|
||||
def estimate_total_records(self, models_data, global_filters, related_keys, user):
|
||||
"""Estima el total de registros para todos los modelos"""
|
||||
total = 0
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
filters = self.apply_related_filters(global_filters, model, related_keys, user)
|
||||
total += model.objects.filter(**filters).count()
|
||||
except:
|
||||
continue
|
||||
return total
|
||||
|
||||
def export_datastage_multiple_to_excel(self, request, models_data, global_filters, related_keys):
|
||||
"""Exporta múltiples modelos de DataStage con filtrado relacionado (múltiples hojas)"""
|
||||
wb = openpyxl.Workbook()
|
||||
wb.remove(wb.active)
|
||||
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
fields = model_data.get('fields', [])
|
||||
|
||||
if not model_name or not fields:
|
||||
continue
|
||||
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
|
||||
# 🔥 APLICAR FILTROS RELACIONADOS
|
||||
filters = self.apply_related_filters(global_filters, model, related_keys, request.user)
|
||||
|
||||
print(f"🎯 FILTROS APLICADOS A {model_name}: {filters}")
|
||||
|
||||
# Si hay filtros, aplicarlos; si no, obtener todos los registros
|
||||
if filters:
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
else:
|
||||
queryset = model.objects.none() # No obtener nada si no hay filtros
|
||||
|
||||
print(f"📊 {model_name}: {queryset.count()} registros después de filtrado relacionado")
|
||||
|
||||
# Si no hay registros, saltar este modelo
|
||||
if queryset.count() == 0:
|
||||
print(f"🔶 {model_name}: Sin registros después de filtrado, saltando...")
|
||||
continue
|
||||
|
||||
# Crear hoja (limitar nombre a 31 caracteres)
|
||||
sheet_name = model_name[:31]
|
||||
ws = wb.create_sheet(title=sheet_name)
|
||||
|
||||
# Escribir encabezados
|
||||
ws.append(fields)
|
||||
|
||||
# Escribir datos
|
||||
for row in queryset:
|
||||
row_values = []
|
||||
for field in fields:
|
||||
value = row[field]
|
||||
# 🔥 USAR safe_excel_value para convertir valores
|
||||
row_values.append(self.safe_excel_value(value))
|
||||
ws.append(row_values)
|
||||
|
||||
except LookupError:
|
||||
print(f"Modelo {model_name} no encontrado")
|
||||
continue
|
||||
|
||||
# Si no se crearon hojas, crear una vacía
|
||||
if len(wb.sheetnames) == 0:
|
||||
ws = wb.create_sheet(title="Sin datos")
|
||||
ws.append(["No se encontraron datos para los modelos especificados"])
|
||||
|
||||
output = io.BytesIO()
|
||||
wb.save(output)
|
||||
output.seek(0)
|
||||
|
||||
response = HttpResponse(
|
||||
output.read(),
|
||||
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
)
|
||||
response['Content-Disposition'] = 'attachment; filename="datastage_related_report.xlsx"'
|
||||
return response
|
||||
|
||||
def export_datastage_multiple_partitioned_excel(self, request, models_data, global_filters, related_keys):
|
||||
"""Exporta múltiples modelos de DataStage a múltiples archivos Excel particionados"""
|
||||
try:
|
||||
zip_buffer = io.BytesIO()
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
fields = model_data.get('fields', [])
|
||||
|
||||
if not model_name or not fields:
|
||||
continue
|
||||
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
filters = self.apply_related_filters(global_filters, model, related_keys, request.user)
|
||||
|
||||
# Si hay filtros, aplicarlos; si no, obtener todos los registros
|
||||
if filters:
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
else:
|
||||
queryset = model.objects.none() # No obtener nada si no hay filtros
|
||||
|
||||
total_records = queryset.count()
|
||||
|
||||
if total_records == 0:
|
||||
print(f"🔶 {model_name}: Sin registros, saltando...")
|
||||
continue
|
||||
|
||||
if total_records > self.MAX_RECORDS_PER_FILE:
|
||||
from django.core.paginator import Paginator
|
||||
paginator = Paginator(queryset, self.MAX_RECORDS_PER_FILE)
|
||||
|
||||
for page_num in paginator.page_range:
|
||||
page = paginator.page(page_num)
|
||||
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = f"Parte_{page_num}"[:31]
|
||||
|
||||
ws.append(fields)
|
||||
|
||||
for row in page.object_list:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
ws.append(row_values)
|
||||
|
||||
# Guardar parte en ZIP
|
||||
part_buffer = io.BytesIO()
|
||||
wb.save(part_buffer)
|
||||
part_buffer.seek(0)
|
||||
|
||||
filename = f"{model_name}_part{page_num}.xlsx"
|
||||
zip_file.writestr(filename, part_buffer.getvalue())
|
||||
|
||||
else:
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Datos"[:31]
|
||||
|
||||
ws.append(fields)
|
||||
|
||||
# Escribir datos
|
||||
for row in queryset:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
ws.append(row_values)
|
||||
|
||||
part_buffer = io.BytesIO()
|
||||
wb.save(part_buffer)
|
||||
part_buffer.seek(0)
|
||||
|
||||
filename = f"{model_name}.xlsx"
|
||||
zip_file.writestr(filename, part_buffer.getvalue())
|
||||
|
||||
except LookupError as e:
|
||||
continue
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
zip_buffer.seek(0)
|
||||
zip_content = zip_buffer.getvalue()
|
||||
|
||||
response = HttpResponse(zip_content, content_type='application/zip')
|
||||
response['Content-Disposition'] = 'attachment; filename="datastage_reports.zip"'
|
||||
response['Content-Length'] = len(zip_content)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': f'Error en exportación particionada: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def export_datastage_multiple_to_csv(self, request, models_data, global_filters, related_keys):
|
||||
"""Exporta múltiples modelos de DataStage a múltiples archivos CSV en ZIP"""
|
||||
zip_buffer = io.BytesIO()
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
fields = model_data.get('fields', [])
|
||||
|
||||
if not model_name or not fields:
|
||||
continue
|
||||
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
filters = self.apply_related_filters(global_filters, model, related_keys, request.user)
|
||||
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
total_records = queryset.count()
|
||||
|
||||
if total_records == 0:
|
||||
continue
|
||||
|
||||
csv_buffer = io.StringIO()
|
||||
writer = csv.writer(csv_buffer)
|
||||
writer.writerow(fields)
|
||||
|
||||
for row in queryset:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
writer.writerow(row_values)
|
||||
|
||||
# Agregar al ZIP
|
||||
filename = f"{model_name}.csv"
|
||||
zip_file.writestr(filename, csv_buffer.getvalue())
|
||||
|
||||
except LookupError:
|
||||
continue
|
||||
|
||||
zip_buffer.seek(0)
|
||||
|
||||
response = HttpResponse(zip_buffer.read(), content_type='application/zip')
|
||||
response['Content-Disposition'] = 'attachment; filename="datastage_reports.zip"'
|
||||
return response
|
||||
|
||||
def export_datastage_multiple_partitioned_csv(self, request, models_data, global_filters, related_keys):
|
||||
"""Exporta múltiples modelos de DataStage a múltiples archivos CSV particionados en ZIP"""
|
||||
try:
|
||||
zip_buffer = io.BytesIO()
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
fields = model_data.get('fields', [])
|
||||
|
||||
if not model_name or not fields:
|
||||
continue
|
||||
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
filters = self.apply_related_filters(global_filters, model, related_keys, request.user)
|
||||
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
total_records = queryset.count()
|
||||
|
||||
if total_records == 0:
|
||||
continue
|
||||
|
||||
if total_records > self.MAX_RECORDS_PER_FILE:
|
||||
from django.core.paginator import Paginator
|
||||
paginator = Paginator(queryset, self.MAX_RECORDS_PER_FILE)
|
||||
|
||||
for page_num in paginator.page_range:
|
||||
page = paginator.page(page_num)
|
||||
|
||||
csv_buffer = io.StringIO()
|
||||
writer = csv.writer(csv_buffer)
|
||||
|
||||
writer.writerow(fields)
|
||||
|
||||
for row in page.object_list:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
writer.writerow(row_values)
|
||||
|
||||
# Agregar al ZIP
|
||||
filename = f"{model_name}_part{page_num}.csv"
|
||||
zip_file.writestr(filename, csv_buffer.getvalue())
|
||||
|
||||
else:
|
||||
# Modelo pequeño, exportar completo
|
||||
csv_buffer = io.StringIO()
|
||||
writer = csv.writer(csv_buffer)
|
||||
|
||||
# Escribir encabezados
|
||||
writer.writerow(fields)
|
||||
|
||||
# Escribir datos
|
||||
for row in queryset:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
writer.writerow(row_values)
|
||||
|
||||
# Agregar al ZIP
|
||||
filename = f"{model_name}.csv"
|
||||
zip_file.writestr(filename, csv_buffer.getvalue())
|
||||
|
||||
except LookupError as e:
|
||||
continue
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
zip_buffer.seek(0)
|
||||
|
||||
response = HttpResponse(zip_buffer.read(), content_type='application/zip')
|
||||
response['Content-Disposition'] = 'attachment; filename="datastage_reports.zip"'
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': f'Error en exportación CSV particionada: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def export_single_model_partitioned(self, request, model_name, fields, filters, total_records):
|
||||
"""Exporta un solo modelo particionado a ZIP"""
|
||||
try:
|
||||
zip_buffer = io.BytesIO()
|
||||
module = 'datastage'
|
||||
|
||||
model = apps.get_model(module, model_name)
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
from django.core.paginator import Paginator
|
||||
paginator = Paginator(queryset, self.MAX_RECORDS_PER_FILE)
|
||||
|
||||
for page_num in paginator.page_range:
|
||||
page = paginator.page(page_num)
|
||||
|
||||
# Crear Excel para esta parte
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = f"Parte_{page_num}"[:31]
|
||||
ws.append(fields)
|
||||
|
||||
for row in page.object_list:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
ws.append(row_values)
|
||||
|
||||
part_buffer = io.BytesIO()
|
||||
wb.save(part_buffer)
|
||||
part_buffer.seek(0)
|
||||
|
||||
filename = f"{model_name}_part{page_num}.xlsx"
|
||||
zip_file.writestr(filename, part_buffer.getvalue())
|
||||
|
||||
zip_buffer.seek(0)
|
||||
zip_content = zip_buffer.getvalue()
|
||||
|
||||
response = HttpResponse(zip_content, content_type='application/zip')
|
||||
response['Content-Disposition'] = f'attachment; filename="{model_name}_particionado.zip"'
|
||||
response['Content-Length'] = len(zip_content)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': f'Error exportando modelo: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def export_single_model_csv_partitioned(self, request, model_name, fields, filters, total_records):
|
||||
"""Exporta un solo modelo CSV particionado a ZIP"""
|
||||
try:
|
||||
zip_buffer = io.BytesIO()
|
||||
module = 'datastage'
|
||||
|
||||
model = apps.get_model(module, model_name)
|
||||
queryset = model.objects.filter(**filters).values(*fields)
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
from django.core.paginator import Paginator
|
||||
paginator = Paginator(queryset, self.MAX_RECORDS_PER_FILE)
|
||||
|
||||
for page_num in paginator.page_range:
|
||||
page = paginator.page(page_num)
|
||||
|
||||
csv_buffer = io.StringIO()
|
||||
writer = csv.writer(csv_buffer)
|
||||
writer.writerow(fields)
|
||||
|
||||
for row in page.object_list:
|
||||
row_values = [self.safe_excel_value(row[field]) for field in fields]
|
||||
writer.writerow(row_values)
|
||||
|
||||
# Agregar al ZIP
|
||||
filename = f"{model_name}_part{page_num}.csv"
|
||||
zip_file.writestr(filename, csv_buffer.getvalue())
|
||||
|
||||
zip_buffer.seek(0)
|
||||
|
||||
zip_content = zip_buffer.getvalue()
|
||||
|
||||
response = HttpResponse(zip_content, content_type='application/zip')
|
||||
response['Content-Disposition'] = f'attachment; filename="{model_name}_particionado.zip"'
|
||||
response['Content-Length'] = len(zip_content)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
return Response({'error': f'Error exportando modelo CSV: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
def get_related_keys_from_filters(self, global_filters, models_data, user):
|
||||
"""
|
||||
Obtiene patentes, pedimentos y datastages que cumplen EXACTAMENTE con TODOS los filtros globales
|
||||
para usarlos como relación entre modelos
|
||||
"""
|
||||
|
||||
related_keys = {
|
||||
'patentes': set(),
|
||||
'pedimentos': set(),
|
||||
'datastage_ids': set()
|
||||
}
|
||||
|
||||
# Si no hay filtros globales, retornar vacío (no hay relación)
|
||||
if not any(global_filters.values()):
|
||||
return {}
|
||||
|
||||
all_records_with_filters = []
|
||||
|
||||
# Buscar en TODOS los modelos que puedan tener los campos de filtro
|
||||
for model_data in models_data:
|
||||
model_name = model_data.get('model')
|
||||
try:
|
||||
model = apps.get_model('datastage', model_name)
|
||||
model_fields = [f.name for f in model._meta.get_fields()]
|
||||
|
||||
# Construir filtros EXACTOS con TODOS los campos disponibles
|
||||
filters = {}
|
||||
has_any_filter = False
|
||||
|
||||
if 'organizacion' in model_fields and global_filters.get('organizacion'):
|
||||
filters['organizacion'] = global_filters['organizacion']
|
||||
has_any_filter = True
|
||||
|
||||
if 'patente' in model_fields and global_filters.get('patente'):
|
||||
filters['patente'] = global_filters['patente']
|
||||
has_any_filter = True
|
||||
|
||||
if 'pedimento' in model_fields and global_filters.get('pedimento'):
|
||||
filters['pedimento'] = global_filters['pedimento']
|
||||
has_any_filter = True
|
||||
|
||||
if 'rfc' in model_fields and global_filters.get('rfc'):
|
||||
filters['rfc'] = global_filters['rfc']
|
||||
has_any_filter = True
|
||||
|
||||
if 'fecha_pago_real' in model_fields:
|
||||
if global_filters.get('fecha_pago_desde'):
|
||||
filters['fecha_pago_real__gte'] = global_filters['fecha_pago_desde']
|
||||
has_any_filter = True
|
||||
|
||||
if global_filters.get('fecha_pago_hasta'):
|
||||
filters['fecha_pago_real__lte'] = global_filters['fecha_pago_hasta']
|
||||
has_any_filter = True
|
||||
|
||||
if has_any_filter:
|
||||
records = model.objects.filter(**filters).values('patente', 'pedimento', 'datastage_id')
|
||||
record_count = records.count()
|
||||
all_records_with_filters.extend(list(records))
|
||||
|
||||
except LookupError:
|
||||
continue
|
||||
|
||||
if not all_records_with_filters:
|
||||
return {'patentes': set(), 'pedimentos': set(), 'datastage_ids': set()}
|
||||
|
||||
for record in all_records_with_filters:
|
||||
if record.get('patente'):
|
||||
related_keys['patentes'].add(record['patente'])
|
||||
if record.get('pedimento'):
|
||||
related_keys['pedimentos'].add(record['pedimento'])
|
||||
if record.get('datastage_id'):
|
||||
related_keys['datastage_ids'].add(record['datastage_id'])
|
||||
|
||||
related_keys = {k: list(v) for k, v in related_keys.items() if v}
|
||||
|
||||
return related_keys
|
||||
|
||||
def apply_global_filters_to_model(self, global_filters, model, user):
|
||||
"""
|
||||
Aplica filtros globales específicamente para modelos DataStage (modo simple)
|
||||
"""
|
||||
filters = {}
|
||||
model_fields = [f.name for f in model._meta.get_fields()]
|
||||
|
||||
if 'organizacion' in model_fields and global_filters.get('organizacion'):
|
||||
filters['organizacion'] = global_filters['organizacion']
|
||||
|
||||
if 'patente' in model_fields and global_filters.get('patente'):
|
||||
filters['patente'] = global_filters['patente']
|
||||
|
||||
if 'pedimento' in model_fields and global_filters.get('pedimento'):
|
||||
filters['pedimento'] = global_filters['pedimento']
|
||||
|
||||
if 'rfc' in model_fields and global_filters.get('rfc'):
|
||||
filters['rfc'] = global_filters['rfc']
|
||||
|
||||
if 'fecha_pago_real' in model_fields:
|
||||
if global_filters.get('fecha_pago_desde'):
|
||||
filters['fecha_pago_real__gte'] = global_filters['fecha_pago_desde']
|
||||
|
||||
if global_filters.get('fecha_pago_hasta'):
|
||||
filters['fecha_pago_real__lte'] = global_filters['fecha_pago_hasta']
|
||||
|
||||
return filters
|
||||
|
||||
def apply_related_filters(self, global_filters, model, related_keys, user):
|
||||
"""
|
||||
Aplica filtros relacionados basados en campos comunes de manera ESTRICTA - VERSIÓN CORREGIDA
|
||||
"""
|
||||
filters = {}
|
||||
model_fields = [f.name for f in model._meta.get_fields()]
|
||||
|
||||
print(f"🔍 APLICANDO FILTROS RELACIONADOS PARA: {model.__name__}")
|
||||
print(f"🔍 CLAVES RELACIONADAS DISPONIBLES: {related_keys}")
|
||||
|
||||
# 🔥 ESTRATEGIA MEJORADA: Usar claves relacionadas SI HAY, sino aplicar filtros directos SOLO si existen
|
||||
has_related_keys = any(related_keys.values())
|
||||
|
||||
if has_related_keys:
|
||||
# 🔥 MODO RELACIONADO ESTRICTO: Usar SOLO las claves obtenidas
|
||||
print("🎯 MODO RELACIONADO ACTIVO - FILTRANDO POR CLAVES COMUNES")
|
||||
|
||||
# Crear condiciones para las claves relacionadas
|
||||
from django.db.models import Q
|
||||
related_conditions = Q()
|
||||
has_related_conditions = False
|
||||
|
||||
if related_keys.get('patentes') and 'patente' in model_fields:
|
||||
filters['patente__in'] = related_keys['patentes']
|
||||
print(f"✅ FILTRO PATENTE RELACIONADO: {len(related_keys['patentes'])} patentes")
|
||||
has_related_conditions = True
|
||||
|
||||
if related_keys.get('pedimentos') and 'pedimento' in model_fields:
|
||||
filters['pedimento__in'] = related_keys['pedimentos']
|
||||
print(f"✅ FILTRO PEDIMENTO RELACIONADO: {len(related_keys['pedimentos'])} pedimentos")
|
||||
has_related_conditions = True
|
||||
|
||||
if related_keys.get('datastage_ids') and 'datastage_id' in model_fields:
|
||||
filters['datastage_id__in'] = related_keys['datastage_ids']
|
||||
print(f"✅ FILTRO DATASTAGE RELACIONADO: {len(related_keys['datastage_ids'])} IDs")
|
||||
has_related_conditions = True
|
||||
|
||||
# Si NO HAY condiciones relacionadas para este modelo (no tiene los campos)
|
||||
if not has_related_conditions:
|
||||
print(f"⚠️ {model.__name__} no tiene campos para aplicar filtros relacionados - RETORNANDO VACÍO")
|
||||
return {} # Retornar filtro vacío hará que no se obtengan registros
|
||||
|
||||
else:
|
||||
# 🔥 MODO DIRECTO: No hay claves relacionadas, aplicar filtros directos SOLO si existen
|
||||
print("🎯 MODO DIRECTO - APLICANDO FILTROS INDIVIDUALES")
|
||||
|
||||
if 'organizacion' in model_fields and global_filters.get('organizacion'):
|
||||
filters['organizacion'] = global_filters['organizacion']
|
||||
print(f"✅ FILTRO ORGANIZACIÓN DIRECTO: {global_filters['organizacion']}")
|
||||
|
||||
if 'patente' in model_fields and global_filters.get('patente'):
|
||||
filters['patente'] = global_filters['patente']
|
||||
print(f"✅ FILTRO PATENTE DIRECTO: {global_filters['patente']}")
|
||||
|
||||
if 'pedimento' in model_fields and global_filters.get('pedimento'):
|
||||
filters['pedimento'] = global_filters['pedimento']
|
||||
print(f"✅ FILTRO PEDIMENTO DIRECTO: {global_filters['pedimento']}")
|
||||
|
||||
if 'rfc' in model_fields and global_filters.get('rfc'):
|
||||
filters['rfc'] = global_filters['rfc']
|
||||
print(f"✅ FILTRO RFC DIRECTO: {global_filters['rfc']}")
|
||||
|
||||
# 🔥 APLICAR ORGANIZACIÓN SIEMPRE si existe (en ambos modos)
|
||||
if 'organizacion' in model_fields and global_filters.get('organizacion'):
|
||||
filters['organizacion'] = global_filters['organizacion']
|
||||
print(f"✅ FILTRO ORGANIZACIÓN: {global_filters['organizacion']}")
|
||||
|
||||
# 🔥 APLICAR FILTROS DE FECHA SIEMPRE (si el campo existe)
|
||||
if 'fecha_pago_real' in model_fields:
|
||||
if global_filters.get('fecha_pago_desde'):
|
||||
filters['fecha_pago_real__gte'] = global_filters['fecha_pago_desde']
|
||||
print(f"✅ FILTRO FECHA DESDE: {global_filters['fecha_pago_desde']}")
|
||||
|
||||
if global_filters.get('fecha_pago_hasta'):
|
||||
filters['fecha_pago_real__lte'] = global_filters['fecha_pago_hasta']
|
||||
print(f"✅ FILTRO FECHA HASTA: {global_filters['fecha_pago_hasta']}")
|
||||
|
||||
print(f"🎯 FILTROS FINALES PARA {model.__name__}: {filters}")
|
||||
|
||||
return filters
|
||||
|
||||
class ExportModelView(APIView):
|
||||
my_tags = ['Reportes']
|
||||
permission_classes = [IsAuthenticated & (
|
||||
IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
|
||||
Reference in New Issue
Block a user