from functools import wraps from django.shortcuts import get_object_or_404, redirect from django.contrib import messages from django.utils.translation import gettext_lazy as _ from django.core.exceptions import PermissionDenied from .models import Warehouse, WarehouseLocation, Product from common.models import UserConfig def get_user_config(user): """ Récupère ou crée la configuration utilisateur. """ if not user.is_authenticated or not user.pk: return None try: return user.config except UserConfig.DoesNotExist: return UserConfig.objects.create(user=user) def user_can_view_warehouse(user, warehouse): """ Vérifie si l'utilisateur peut visualiser un entrepôt. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_view_warehouse(warehouse) def user_can_manage_warehouse(user, warehouse): """ Vérifie si l'utilisateur peut gérer un entrepôt. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_manage_warehouse(warehouse) def get_user_accessible_warehouses(user, can_manage=False): """ Retourne la liste des entrepôts accessibles par l'utilisateur. """ if not user.is_authenticated: return Warehouse.objects.none() config = get_user_config(user) if not config: return Warehouse.objects.none() return config.get_accessible_warehouses(can_manage=can_manage) def warehouse_view_required(view_func): """ Décorateur pour vérifier que l'utilisateur peut visualiser l'entrepôt. L'ID de l'entrepôt doit être passé en paramètre 'warehouse_id'. """ @wraps(view_func) def wrapper(request, warehouse_id, *args, **kwargs): warehouse = get_object_or_404(Warehouse, id=warehouse_id) if not user_can_view_warehouse(request.user, warehouse): messages.error( request, _("Vous n'avez pas la permission de visualiser cet entrepôt.") ) return redirect('stock:warehouses') return view_func(request, warehouse_id, *args, **kwargs) return wrapper def warehouse_manage_required(view_func): """ Décorateur pour vérifier que l'utilisateur peut gérer l'entrepôt. L'ID de l'entrepôt doit être passé en paramètre 'warehouse_id'. """ @wraps(view_func) def wrapper(request, warehouse_id, *args, **kwargs): warehouse = get_object_or_404(Warehouse, id=warehouse_id) if not user_can_manage_warehouse(request.user, warehouse): messages.error( request, _("Vous n'avez pas la permission de gérer cet entrepôt.") ) return redirect('stock:warehouses') return view_func(request, warehouse_id, *args, **kwargs) return wrapper def location_view_required(view_func): """ Décorateur pour vérifier que l'utilisateur peut visualiser l'emplacement d'entrepôt. L'ID de l'emplacement doit être passé en paramètre 'location_id'. """ @wraps(view_func) def wrapper(request, location_id, *args, **kwargs): location = get_object_or_404(WarehouseLocation, id=location_id) if not user_can_view_warehouse(request.user, location.warehouse): messages.error( request, _("Vous n'avez pas la permission de visualiser cet emplacement.") ) return redirect('stock:warehouses') return view_func(request, location_id, *args, **kwargs) return wrapper def location_manage_required(view_func): """ Décorateur pour vérifier que l'utilisateur peut gérer l'emplacement d'entrepôt. L'ID de l'emplacement doit être passé en paramètre 'location_id'. """ @wraps(view_func) def wrapper(request, location_id, *args, **kwargs): location = get_object_or_404(WarehouseLocation, id=location_id) if not user_can_manage_warehouse(request.user, location.warehouse): messages.error( request, _("Vous n'avez pas la permission de gérer cet emplacement.") ) return redirect('stock:warehouses') return view_func(request, location_id, *args, **kwargs) return wrapper def product_view_required(view_func): """ Décorateur pour vérifier que l'utilisateur peut visualiser un produit. Vérifie que l'utilisateur a accès à au moins un entrepôt contenant ce produit. L'ID du produit doit être passé en paramètre 'product_id'. """ @wraps(view_func) def wrapper(request, product_id, *args, **kwargs): product = get_object_or_404(Product, id=product_id) # Bypasser pour les superutilisateurs, le staff ou les admins config = get_user_config(request.user) if request.user.is_superuser or request.user.is_staff or (config and config.has_role('admin')): return view_func(request, product_id, *args, **kwargs) # Bypasser si le produit n'a aucun stock physique n'importe où if not product.stocks.exists(): return view_func(request, product_id, *args, **kwargs) accessible_warehouses = get_user_accessible_warehouses(request.user) # Vérifier si le produit est dans un entrepôt accessible product_warehouses = Warehouse.objects.filter( locations__stocks__product=product ).distinct() if not product_warehouses.filter(id__in=accessible_warehouses).exists(): messages.error( request, _("Vous n'avez pas la permission de visualiser ce produit.") ) return redirect('stock:warehouses') return view_func(request, product_id, *args, **kwargs) return wrapper def filter_warehouses_by_permission(queryset, user, can_manage=False): """ Filtre un queryset de Warehouse en fonction des permissions de l'utilisateur. """ accessible_warehouses = get_user_accessible_warehouses(user, can_manage=can_manage) return queryset.filter(id__in=accessible_warehouses) def filter_locations_by_permission(queryset, user, can_manage=False): """ Filtre un queryset de WarehouseLocation en fonction des permissions de l'utilisateur. """ accessible_warehouses = get_user_accessible_warehouses(user, can_manage=can_manage) return queryset.filter(warehouse__id__in=accessible_warehouses) def filter_products_by_permission(queryset, user): """ Filtre un queryset de Product en fonction des permissions de l'utilisateur. Retourne les produits présents dans les entrepôts accessibles, ainsi que les produits qui ne sont pas encore associés à un emplacement (sans stock). """ from django.db.models import Q accessible_warehouses = get_user_accessible_warehouses(user) return queryset.filter( Q(stocks__isnull=True) | Q(stocks__location__warehouse__id__in=accessible_warehouses) ).distinct() def user_can_validate_inventory(user, warehouse): """ Vérifie si l'utilisateur peut valider les inventaires d'un entrepôt. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_validate_inventory_for(warehouse) def user_can_perform_inventory_on(user, location): """ Vérifie si l'utilisateur peut effectuer un inventaire sur un emplacement spécifique. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_perform_inventory_on(location) def user_can_validate_inventory_on(user, location): """ Vérifie si l'utilisateur peut valider les inventaires d'un emplacement spécifique. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_validate_inventory_on(location) def user_can_administer_warehouse(user, warehouse): """ Vérifie si l'utilisateur peut administrer un entrepôt spécifique. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_administer_warehouse(warehouse) def user_can_administer_any_warehouse(user): """ Vérifie si l'utilisateur peut administrer au moins un entrepôt. """ if not user.is_authenticated: return False config = get_user_config(user) if not config: return False return config.can_administer_any_warehouse() def user_can_manage_products(user): """ Vérifie si l'utilisateur peut gérer les produits (créer, modifier, supprimer). Autorisé pour les rôles admin et pour les utilisateurs ayant can_administer sur au moins un entrepôt. """ if not user.is_authenticated: return False if user.is_superuser: return True config = get_user_config(user) if not config: return False return ( config.has_role('admin') or config.can_administer_any_warehouse() )