feature/rbac permisos y roles implementados
This commit is contained in:
@@ -1,57 +1,31 @@
|
||||
from warnings import filters
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from api.customs.models import Pedimento, Cove, EDocument, Partida
|
||||
from api.record.models import Document
|
||||
from api.organization.models import Organizacion
|
||||
from django.db.models import Count, Q
|
||||
|
||||
# Registrar endpoint en urls.py:
|
||||
# path('dashboard/summary/', dashboard_summary)
|
||||
import csv
|
||||
import io
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from .serializers import ExportModelSerializer
|
||||
from rest_framework.response import Response
|
||||
from django.http import HttpResponse
|
||||
import openpyxl
|
||||
from django.apps import apps
|
||||
from rest_framework import status
|
||||
from django.shortcuts import render
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .serializers import ExportModelSerializer
|
||||
from core.permissions import (
|
||||
IsSameOrganization,
|
||||
IsSameOrganizationDeveloper,
|
||||
IsSameOrganizationAndAdmin,
|
||||
IsSuperUser
|
||||
)
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
import csv
|
||||
import io
|
||||
import openpyxl
|
||||
from django.http import HttpResponse
|
||||
from django.apps import apps
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from core.permissions import (
|
||||
IsSameOrganization,
|
||||
IsSameOrganizationDeveloper,
|
||||
IsSameOrganizationAndAdmin,
|
||||
IsSuperUser
|
||||
)
|
||||
from .serializers import ExportModelSerializer
|
||||
import uuid
|
||||
import datetime
|
||||
import zipfile
|
||||
|
||||
import openpyxl
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.models import Count, Q
|
||||
from django.http import HttpResponse
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from api.customs.models import Cove, EDocument, Partida, Pedimento
|
||||
from api.organization.models import Organizacion
|
||||
from api.record.models import Document
|
||||
from core.permissions import (
|
||||
get_org_context,
|
||||
require_permission,
|
||||
user_has_permission,
|
||||
)
|
||||
from .serializers import ExportModelSerializer
|
||||
|
||||
def export_model_to_csv(request, model_name, fields, module='datastage', filters=None):
|
||||
model = apps.get_model(module, model_name)
|
||||
@@ -110,7 +84,11 @@ def export_model_to_excel(request, model_name, fields, module='datastage', filte
|
||||
|
||||
class ExportDataStageView(APIView):
|
||||
my_tags = ['Reportes-DataStage']
|
||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == 'GET':
|
||||
return [IsAuthenticated(), require_permission('reportes.view')()]
|
||||
return [IsAuthenticated(), require_permission('reportes.export')()]
|
||||
|
||||
# Constantes para partición
|
||||
# MAX_RECORDS_PER_FILE = 100 # Límite seguro por archivo
|
||||
@@ -136,20 +114,14 @@ class ExportDataStageView(APIView):
|
||||
return str(value)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Retorna RFCs distintos de Registro501 para la organización indicada. El parámetro organizacion es obligatorio."""
|
||||
"""Retorna RFCs distintos de Registro501 para la organización activa del usuario."""
|
||||
try:
|
||||
Registro501 = apps.get_model('datastage', 'Registro501')
|
||||
|
||||
if not request.user.is_superuser:
|
||||
qs = Registro501.objects.filter(organizacion=request.user.organizacion)
|
||||
else:
|
||||
org_id = request.query_params.get('organizacion')
|
||||
if not org_id:
|
||||
return Response({'error': 'El parámetro organizacion es obligatorio'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
qs = Registro501.objects.filter(organizacion_id=uuid.UUID(org_id))
|
||||
except (ValueError, AttributeError):
|
||||
return Response({'error': 'UUID de organización inválido'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
org = get_org_context(request.user)
|
||||
if not org:
|
||||
return Response({'error': 'Sin organización activa'}, status=status.HTTP_403_FORBIDDEN)
|
||||
qs = Registro501.objects.filter(organizacion=org)
|
||||
|
||||
rfcs = (
|
||||
qs.exclude(rfc__isnull=True)
|
||||
@@ -178,23 +150,19 @@ class ExportDataStageView(APIView):
|
||||
def _resolve_org_filter(self, global_filters, user):
|
||||
"""
|
||||
Devuelve los global_filters asegurando que siempre haya una organización.
|
||||
- Superuser sin org → error (no mezclar tenants).
|
||||
- No-superuser sin org → se inyecta la org del usuario.
|
||||
La org se obtiene de active_organization (superuser) o del campo organizacion (usuario normal).
|
||||
Retorna (filters_dict, error_response_or_None).
|
||||
"""
|
||||
org_value = (global_filters or {}).get('organizacion', '')
|
||||
if not org_value:
|
||||
if user.is_superuser:
|
||||
filters = dict(global_filters or {})
|
||||
if not filters.get('organizacion'):
|
||||
org = get_org_context(user)
|
||||
if not org:
|
||||
return None, Response(
|
||||
{'error': 'El parámetro organizacion es obligatorio'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
{'error': 'Sin organización activa'},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
# No-superuser: inyectar su propia org
|
||||
if hasattr(user, 'organizacion') and user.organizacion:
|
||||
filters = dict(global_filters or {})
|
||||
filters['organizacion'] = str(user.organizacion.id)
|
||||
return filters, None
|
||||
return dict(global_filters or {}), None
|
||||
filters['organizacion'] = str(org.id)
|
||||
return filters, None
|
||||
|
||||
def handle_simple_export(self, request):
|
||||
"""Maneja exportación simple de DataStage (un solo modelo)"""
|
||||
@@ -1868,7 +1836,11 @@ class ExportDataStageView(APIView):
|
||||
|
||||
class ExportModelView(APIView):
|
||||
my_tags = ['Reportes']
|
||||
permission_classes = [IsAuthenticated & (IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == 'GET':
|
||||
return [IsAuthenticated(), require_permission('reportes.view')()]
|
||||
return [IsAuthenticated(), require_permission('reportes.export')()]
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
@@ -1906,6 +1878,8 @@ class ExportModelView(APIView):
|
||||
model_name = request.data.get('model')
|
||||
fields = request.data.get('fields')
|
||||
filters = request.data.get('filters', {})
|
||||
org = get_org_context(request.user)
|
||||
filters['organizacion__id'] = org.id if org else None
|
||||
export_type = request.data.get('type', 'csv')
|
||||
module = request.data.get('module', 'datastage')
|
||||
|
||||
@@ -1917,40 +1891,12 @@ class ExportModelView(APIView):
|
||||
else:
|
||||
return export_model_to_csv(request, model_name, fields, module, filters)
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class ExportModelView(APIView):
|
||||
my_tags = ['Reportes']
|
||||
permission_classes = [IsAuthenticated & (
|
||||
IsSameOrganization | IsSameOrganizationAndAdmin | IsSameOrganizationDeveloper | IsSuperUser)]
|
||||
|
||||
@swagger_auto_schema(request_body=ExportModelSerializer, esponses={200: 'Archivo generado (Excel o CSV)'})
|
||||
def post(self, request, *args, **kwargs):
|
||||
model_name = request.data.get('model')
|
||||
fields = request.data.get('fields')
|
||||
filters = request.data.get('filters', {})
|
||||
filters['organizacion__id'] = self.request.user.organizacion.id if hasattr(request.user, 'organizacion') and request.user.organizacion else None
|
||||
export_type = request.data.get('type', 'csv')
|
||||
if not model_name or not fields:
|
||||
return Response({'error': 'model and fields are required'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
module = request.data.get('module', 'datastage')
|
||||
if export_type == 'excel':
|
||||
return export_model_to_excel(request, model_name, fields, module, filters)
|
||||
else:
|
||||
return export_model_to_csv(request, model_name, fields, module, filters)
|
||||
|
||||
# Resumen general para dashboard
|
||||
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes([
|
||||
IsAuthenticated
|
||||
])
|
||||
@permission_classes([IsAuthenticated, require_permission('reportes.view')])
|
||||
def dashboard_summary(request):
|
||||
org_id = request.query_params.get('organizacion_id')
|
||||
filters = {}
|
||||
user = request.user
|
||||
|
||||
@@ -1964,18 +1910,16 @@ def dashboard_summary(request):
|
||||
fecha_pago_lte = request.query_params.get('fecha_pago__lte')
|
||||
contribuyente__rfc = request.query_params.get('contribuyente__rfc')
|
||||
|
||||
# Si no se especifica organización y el usuario tiene organización, usarla
|
||||
if not org_id and hasattr(user, 'organizacion') and user.organizacion:
|
||||
org_id = user.organizacion.id
|
||||
# Si no es superusuario, filtrar por organización
|
||||
if org_id and not getattr(user, 'is_superuser', False):
|
||||
filters['organizacion_id'] = org_id
|
||||
org = get_org_context(user)
|
||||
if not org:
|
||||
return Response({'error': 'Sin organización activa.'}, status=status.HTTP_403_FORBIDDEN)
|
||||
filters['organizacion_id'] = org.id
|
||||
|
||||
# Si el usuario pertenece al grupo Importador, filtrar por RFC
|
||||
if user.groups.filter(name='Importador').exists():
|
||||
rfc = getattr(user, 'rfc', None)
|
||||
if rfc:
|
||||
filters['contribuyente__rfc'] = rfc
|
||||
# Importador: filtrar solo por sus RFC asignados
|
||||
if user.is_importador:
|
||||
rfcs = list(user.rfc.values_list('rfc', flat=True))
|
||||
if rfcs:
|
||||
filters['contribuyente__rfc__in'] = rfcs
|
||||
|
||||
if pedimento_app:
|
||||
filters['pedimento_app'] = pedimento_app
|
||||
|
||||
Reference in New Issue
Block a user