loko/streetup/stock/permissions.py
2026-07-22 14:48:40 +02:00

305 lines
9.3 KiB
Python

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()
)