419 lines
No EOL
13 KiB
Python
419 lines
No EOL
13 KiB
Python
from django.core.exceptions import PermissionDenied
|
||
from django.utils.translation import gettext as _
|
||
|
||
from functools import wraps
|
||
|
||
from common.models import UserContractAccess, UserContractAccess, UserConfig, UserThematics
|
||
from contracts.models import Contract, ContractOrder, ContractOrderDocument, ClaimDeclarationAttachment
|
||
|
||
|
||
|
||
|
||
ALLOWED_ROLES_FOR_CONTRACT_ACCESS = {"admin", "manager", "external_manager", "controller", "top_manager"}
|
||
|
||
def get_allowed_contracts_for_user(user):
|
||
"""
|
||
Retourne une queryset des contrats accessibles à un utilisateur
|
||
s'il a le droit de voir les interventions ET un rôle autorisé.
|
||
"""
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return Contract.objects.none()
|
||
|
||
# Vérifie les rôles
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
|
||
return Contract.objects.none()
|
||
|
||
if "admin" in user_roles or "top_manager" in user_roles:
|
||
return Contract.objects.all()
|
||
|
||
# Filtre les UserContractAccess valides
|
||
allowed_contract_ids = UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
can_view_interventions=True
|
||
).values_list("contract_id", flat=True)
|
||
|
||
return Contract.objects.filter(id__in=allowed_contract_ids)
|
||
|
||
|
||
|
||
def user_has_access_to_contract(user, contract_id):
|
||
"""
|
||
Vérifie si un utilisateur peut accéder à un contrat donné :
|
||
- A un rôle autorisé
|
||
- A un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
|
||
return False
|
||
|
||
if "admin" in user_roles or "top_manager" in user_roles:
|
||
return True
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
def user_can_view_contract_prices(user, contract_id):
|
||
"""Vrai si l'utilisateur est admin ou dispose du droit can_view_prices sur le contrat."""
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
|
||
return False
|
||
|
||
if "admin" in user_roles or "top_manager" in user_roles:
|
||
return True
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_prices=True
|
||
).exists()
|
||
|
||
|
||
def contract_view_required(view_func):
|
||
"""
|
||
Décorateur basé sur user_has_access_to_contract.
|
||
La vue doit avoir un paramètre `contract_id` (en keyword arg).
|
||
"""
|
||
|
||
@wraps(view_func)
|
||
def _wrapped_view(request, *args, **kwargs):
|
||
contract_id = kwargs.get("contract_id")
|
||
if contract_id is None:
|
||
raise PermissionDenied(_("Paramètre 'contract_id' manquant."))
|
||
|
||
from .permissions import user_has_access_to_contract # éviter import circulaire
|
||
|
||
if not user_has_access_to_contract(request.user, contract_id):
|
||
raise PermissionDenied(_("Accès au contrat refusé."))
|
||
|
||
return view_func(request, *args, **kwargs)
|
||
|
||
return _wrapped_view
|
||
|
||
|
||
def user_has_access_to_contract_order(user, order_id):
|
||
"""
|
||
Vérifie que l'utilisateur a accès au contrat lié à une ContractOrder.
|
||
"""
|
||
try:
|
||
order = ContractOrder.objects.select_related('contract').get(id=order_id)
|
||
except ContractOrder.DoesNotExist:
|
||
return False
|
||
|
||
return user_has_access_to_contract(user, order.contract.id)
|
||
|
||
|
||
def order_view_required(view_func):
|
||
"""
|
||
Décorateur pour restreindre l'accès à une vue de ContractOrder.
|
||
Nécessite un paramètre keyword `order_id`.
|
||
"""
|
||
|
||
@wraps(view_func)
|
||
def _wrapped_view(request, *args, **kwargs):
|
||
order_id = kwargs.get("order_id")
|
||
if order_id is None:
|
||
raise PermissionDenied(_("Paramètre 'order_id' manquant."))
|
||
|
||
from .permissions import user_has_access_to_contract_order
|
||
|
||
if not user_has_access_to_contract_order(request.user, order_id):
|
||
raise PermissionDenied(_("Accès à la commande refusé."))
|
||
|
||
return view_func(request, *args, **kwargs)
|
||
|
||
return _wrapped_view
|
||
|
||
|
||
ALLOWED_ROLES_FOR_ORDER_CREATION = {"admin", "manager"}
|
||
|
||
def user_can_add_order(user):
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if user_roles.intersection(ALLOWED_ROLES_FOR_ORDER_CREATION):
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
def user_can_add_order_for_contract(user, contract_id):
|
||
"""
|
||
Vérifie que l'utilisateur a le droit d'ajouter une commande à un contrat donné :
|
||
- Il doit avoir un rôle 'admin' ou 'manager'
|
||
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_ORDER_CREATION):
|
||
return False
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
def user_has_access_to_contract_posts(user, contract_id):
|
||
"""
|
||
Vérifie que l'utilisateur a accès aux postes du contrat.
|
||
"""
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_CONTRACT_ACCESS):
|
||
return False
|
||
|
||
if "admin" in user_roles or "top_manager" in user_roles:
|
||
return True
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_posts=True
|
||
).exists()
|
||
|
||
|
||
ALLOWED_ROLES_FOR_QUOTE_MANAGEMENT = {"admin", "external_manager"}
|
||
|
||
def user_can_manage_quote_for_contract(user, contract_id):
|
||
"""
|
||
Vérifie que l'utilisateur a le droit de gérer le devis d'une commande :
|
||
- Il doit avoir un rôle 'admin' ou 'external_manager'
|
||
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_QUOTE_MANAGEMENT):
|
||
return False
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
def user_can_manage_direct_order_posts(user, contract_id):
|
||
"""
|
||
Vérifie que l'utilisateur peut créer/modifier/supprimer des commandes directes :
|
||
- Il doit avoir le rôle 'external_manager'
|
||
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
if not config.roles.filter(name='external_manager').exists():
|
||
return False
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
ALLOWED_ROLES_FOR_DIRECT_POST_VALIDATION = {"admin", "manager", "controller"}
|
||
|
||
def user_can_validate_direct_order_posts(user, contract_id):
|
||
"""
|
||
Vérifie que l'utilisateur peut valider/refuser des commandes directes :
|
||
- Il doit avoir un rôle 'admin', 'manager' ou 'controller'
|
||
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection(ALLOWED_ROLES_FOR_DIRECT_POST_VALIDATION):
|
||
return False
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=contract_id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
def user_can_generate_order_documents(user, order):
|
||
"""
|
||
Vérifie que l'utilisateur a le droit de générer des documents pour une commande donnée :
|
||
- Il doit avoir un rôle 'admin' ou 'external_manager'
|
||
- Il doit avoir un accès UserContractAccess avec can_view_interventions=True
|
||
"""
|
||
if not user.is_authenticated:
|
||
return False
|
||
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if not user_roles.intersection({"admin", "external_manager"}):
|
||
return False
|
||
|
||
return UserContractAccess.objects.filter(
|
||
user_config=config,
|
||
contract_id=order.contract.id,
|
||
can_view_interventions=True
|
||
).exists()
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Permissions équipes (CompanyTeam / CompanyMember)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def get_viewable_teams_for_user(user):
|
||
"""
|
||
Retourne le queryset des équipes que l'utilisateur peut consulter.
|
||
Les admins voient toutes les équipes ; les autres ont besoin d'un
|
||
UserCompanyTeamAccess avec can_view=True.
|
||
"""
|
||
from contracts.models import CompanyTeam, UserCompanyTeamAccess
|
||
if not user.is_authenticated:
|
||
return CompanyTeam.objects.none()
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return CompanyTeam.objects.none()
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if "admin" in user_roles:
|
||
return CompanyTeam.objects.all()
|
||
return CompanyTeam.objects.filter(
|
||
user_accesses__user_config=config,
|
||
user_accesses__can_view=True,
|
||
)
|
||
|
||
|
||
def user_can_view_teams(user):
|
||
"""Vérifie si l'utilisateur peut accéder à au moins une équipe."""
|
||
from contracts.models import UserCompanyTeamAccess
|
||
if not user.is_authenticated:
|
||
return False
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if "admin" in user_roles:
|
||
return True
|
||
return UserCompanyTeamAccess.objects.filter(
|
||
user_config=config,
|
||
can_view=True,
|
||
).exists()
|
||
|
||
|
||
def user_can_edit_teams(user):
|
||
"""
|
||
Vérifie si l'utilisateur a le droit d'éditer des équipes.
|
||
Les admins ont toujours le droit ; les autres ont besoin d'un
|
||
UserCompanyTeamAccess avec can_edit=True.
|
||
"""
|
||
from contracts.models import UserCompanyTeamAccess
|
||
if not user.is_authenticated:
|
||
return False
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if "admin" in user_roles:
|
||
return True
|
||
return UserCompanyTeamAccess.objects.filter(
|
||
user_config=config,
|
||
can_edit=True,
|
||
).exists()
|
||
|
||
|
||
def user_can_edit_specific_team(user, team_id):
|
||
"""
|
||
Vérifie si l'utilisateur peut éditer une équipe précise (créer/supprimer
|
||
des membres, déplacer des membres vers/depuis cette équipe).
|
||
"""
|
||
from contracts.models import UserCompanyTeamAccess
|
||
if not user.is_authenticated:
|
||
return False
|
||
try:
|
||
config = user.config
|
||
except AttributeError:
|
||
return False
|
||
user_roles = set(config.roles.values_list("name", flat=True))
|
||
if "admin" in user_roles:
|
||
return True
|
||
return UserCompanyTeamAccess.objects.filter(
|
||
user_config=config,
|
||
team_id=team_id,
|
||
can_edit=True,
|
||
).exists()
|
||
|
||
|
||
|
||
|
||
def guard_contract_order_file(obj: ContractOrder, user) -> bool:
|
||
# Accès si l’utilisateur a accès à la commande
|
||
return user_has_access_to_contract_order(user, obj.id)
|
||
|
||
def guard_contract_file(obj: Contract, user) -> bool:
|
||
# Si un jour tu ajoutes un FileField sur Contract
|
||
return user_has_access_to_contract(user, obj.id)
|
||
|
||
def guard_order_document_file(obj: ContractOrderDocument, user) -> bool:
|
||
# Document rattaché à une commande → on réutilise la même permission
|
||
return user_has_access_to_contract_order(user, obj.order_id)
|
||
|
||
|
||
def guard_claim_attachment_file(obj: ClaimDeclarationAttachment, user) -> bool:
|
||
# Pièce jointe de déclaration de créance → accès via la commande du document parent
|
||
return user_has_access_to_contract_order(user, obj.claim_document.order_id)
|
||
|
||
def guard_order_attachment_file(obj, user) -> bool:
|
||
# Pièce jointe générique d'une commande (photos, PDF, …)
|
||
return user_has_access_to_contract_order(user, obj.order_id) |