feat: implement time-restricted intervention visibility and occupation management permissions for teams

This commit is contained in:
Karl Determe 2026-08-20 12:05:51 +02:00
parent 86979307ae
commit a42c46f336
3 changed files with 78 additions and 25 deletions

View file

@ -253,8 +253,6 @@ def can_view_intervention(user, intervention):
return False
# 2. Restriction aux interventions assignées
assigned_only_teams = company_member.teams.filter(restrict_to_assigned_interventions=True)
if assigned_only_teams.exists():
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
unrestricted_companies = set(unrestricted_teams.values_list('company_id', flat=True))
@ -510,11 +508,12 @@ def get_accessible_interventions_for_technician(user):
company_member = CompanyMember.objects.prefetch_related('teams__company', 'visible_maintain_types').filter(user=user).first()
if company_member:
user_companies = company_member.teams.values_list('company_id', flat=True).distinct()
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
unrestricted_companies = unrestricted_teams.values_list('company_id', flat=True).distinct()
qs = base_qs.filter(
Q(assigned_member__user=user) |
Q(assigned_team__in=company_member.teams.all(), status__in=['to_be_processed', 'assigned']) |
Q(assigned_member__isnull=True, assigned_team__isnull=True, assigned_provider_id__in=user_companies, status__in=['to_be_processed', 'assigned'])
Q(assigned_member__isnull=True, assigned_team__isnull=True, assigned_provider_id__in=unrestricted_companies, status__in=['to_be_processed', 'assigned'])
)
# Appliquer le filtre des types de maintenance visibles (sauf pour les interventions assignées)
visible_types = company_member.get_visible_maintain_type_codes()
@ -677,8 +676,7 @@ def filter_viewable_interventions_for_user(user, limit_to_default_thematic=False
user_roles = set(user_config.roles.values_list('name', flat=True)) if user_config else set()
privileged_roles = {'admin', 'top_manager', 'manager', 'controller', 'operator', 'external_manager'}
restricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=True)
if restricted_teams.exists() and not (user_roles & privileged_roles):
if not (user_roles & privileged_roles):
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())
@ -720,8 +718,6 @@ def filter_viewable_interventions_for_user(user, limit_to_default_thematic=False
privileged_roles = {'admin', 'top_manager', 'manager', 'controller', 'operator', 'external_manager'}
if not (user_roles & privileged_roles):
# 1. Restriction aux interventions assignées par équipe
assigned_only_teams = company_member.teams.filter(restrict_to_assigned_interventions=True)
if assigned_only_teams.exists():
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())

View file

@ -386,6 +386,63 @@ class TeamAssignedOnlyVisibilityTests(TestCase):
visible_qs = filter_viewable_interventions_for_user(self.tech_user)
self.assertIn(itv_unassigned, visible_qs)
def test_visibility_no_teams_assigned(self):
from interventions.models import Intervention
from interventions.permissions import can_view_intervention, filter_viewable_interventions_for_user
# Member belongs to NO team (self.member.teams is empty)
self.member.teams.clear()
# 1. Assigned to team
itv_team = Intervention.objects.create(
title='Team Task',
status='to_be_processed',
contract=self.contract,
thematic=self.thematic,
assigned_provider=self.company,
assigned_team=self.team_restricted,
)
# 2. Assigned to member
itv_member = Intervention.objects.create(
title='Member Task',
status='to_be_processed',
contract=self.contract,
thematic=self.thematic,
assigned_provider=self.company,
assigned_member=self.member,
)
# 3. Unassigned company task
itv_unassigned = Intervention.objects.create(
title='Unassigned Task',
status='to_be_processed',
contract=self.contract,
thematic=self.thematic,
assigned_provider=self.company,
)
# 4. Assigned to other team
itv_other = Intervention.objects.create(
title='Other Team Task',
status='to_be_processed',
contract=self.contract,
thematic=self.thematic,
assigned_provider=self.company,
assigned_team=self.other_team,
)
# Check individual visibility: only directly assigned task is visible
self.assertFalse(can_view_intervention(self.tech_user, itv_team))
self.assertTrue(can_view_intervention(self.tech_user, itv_member))
self.assertFalse(can_view_intervention(self.tech_user, itv_unassigned))
self.assertFalse(can_view_intervention(self.tech_user, itv_other))
# Check list visibility: only directly assigned task is visible
visible_qs = filter_viewable_interventions_for_user(self.tech_user)
self.assertEqual(visible_qs.count(), 1)
self.assertIn(itv_member, visible_qs)
self.assertNotIn(itv_team, visible_qs)
self.assertNotIn(itv_unassigned, visible_qs)
self.assertNotIn(itv_other, visible_qs)
def test_is_restricted_team_context(self):
from interventions.models import Intervention
from django.urls import reverse

View file

@ -829,7 +829,7 @@ def intervention_detail_mobile(request, intervention_id):
privileged_roles = {'admin', 'top_manager', 'manager', 'controller', 'operator', 'external_manager'}
if not (user_roles & privileged_roles):
matching_teams = company_member.teams.filter(company=intervention.assigned_provider)
if matching_teams.exists() and not matching_teams.filter(restrict_to_assigned_interventions=False).exists():
if not matching_teams.filter(restrict_to_assigned_interventions=False).exists():
is_restricted_team = True
# --- Timeline mobile : transitions, statuts, rôles ---