feat: add allow_team_members_edit option to contracts and update permissions to allow intra-team intervention management
This commit is contained in:
parent
94cb7148a0
commit
e2eb20536d
8 changed files with 286 additions and 12 deletions
|
|
@ -96,9 +96,9 @@ class CompanyAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(Contract)
|
@admin.register(Contract)
|
||||||
class ContractAdmin(admin.ModelAdmin):
|
class ContractAdmin(admin.ModelAdmin):
|
||||||
list_display = ('contract_number', 'description', 'company', 'start_date', 'end_date', 'is_active', 'contact_phone', 'send_email_notifications', 'send_daily_summary', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'thematics_list')
|
list_display = ('contract_number', 'description', 'company', 'start_date', 'end_date', 'is_active', 'contact_phone', 'send_email_notifications', 'send_daily_summary', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'allow_team_members_edit', 'thematics_list')
|
||||||
search_fields = ('contract_number', 'company__name', 'description', 'contact_phone')
|
search_fields = ('contract_number', 'company__name', 'description', 'contact_phone')
|
||||||
list_filter = ('is_active', 'thematics', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'send_daily_summary')
|
list_filter = ('is_active', 'thematics', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'allow_team_members_edit', 'send_daily_summary')
|
||||||
filter_horizontal = ('thematics',)
|
filter_horizontal = ('thematics',)
|
||||||
|
|
||||||
def get_inlines(self, request, obj=None):
|
def get_inlines(self, request, obj=None):
|
||||||
|
|
@ -117,7 +117,7 @@ class ContractAdmin(admin.ModelAdmin):
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
('Options de workflow', {
|
('Options de workflow', {
|
||||||
'fields': ('needs_approval', 'needs_checking', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'warn_if_no_prestation_on_processed'),
|
'fields': ('needs_approval', 'needs_checking', 'uses_occupations', 'occupations_required', 'uses_intervention_manager', 'warn_if_no_prestation_on_processed', 'allow_team_members_edit'),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-08-19 09:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contracts', '0002_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='contract',
|
||||||
|
name='allow_team_members_edit',
|
||||||
|
field=models.BooleanField(default=False, help_text="Si activé, les membres d'une même équipe peuvent modifier les interventions assignées à d'autres membres de leur équipe, y compris à partir du statut 'Pris en charge' ou au-delà.", verbose_name="Modification inter-membres d'équipe autorisée"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -341,6 +341,11 @@ class Contract(models.Model):
|
||||||
verbose_name=_("Emails uniquement si priorité urgente"),
|
verbose_name=_("Emails uniquement si priorité urgente"),
|
||||||
help_text=_("Si activé, les notifications email ne sont envoyées que pour les interventions de priorité 'Urgent'."),
|
help_text=_("Si activé, les notifications email ne sont envoyées que pour les interventions de priorité 'Urgent'."),
|
||||||
)
|
)
|
||||||
|
allow_team_members_edit = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("Modification inter-membres d'équipe autorisée"),
|
||||||
|
help_text=_("Si activé, les membres d'une même équipe peuvent modifier les interventions assignées à d'autres membres de leur équipe, y compris à partir du statut 'Pris en charge' ou au-delà."),
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Contrat {self.contract_number} - {self.company.name}"
|
return f"Contrat {self.contract_number} - {self.company.name}"
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,14 @@ def can_view_intervention(user, intervention):
|
||||||
is_assigned_to_user = (intervention.assigned_member == company_member)
|
is_assigned_to_user = (intervention.assigned_member == company_member)
|
||||||
is_assigned_to_user_team = (intervention.assigned_team in company_member.teams.all())
|
is_assigned_to_user_team = (intervention.assigned_team in company_member.teams.all())
|
||||||
is_unrestricted_company = (intervention.assigned_provider_id in unrestricted_companies)
|
is_unrestricted_company = (intervention.assigned_provider_id in unrestricted_companies)
|
||||||
|
is_team_member_edit_allowed = bool(
|
||||||
|
intervention.contract
|
||||||
|
and getattr(intervention.contract, 'allow_team_members_edit', False)
|
||||||
|
and intervention.assigned_member
|
||||||
|
and intervention.assigned_member.teams.filter(id__in=company_member.teams.all()).exists()
|
||||||
|
)
|
||||||
|
|
||||||
if not (is_assigned_to_user or is_assigned_to_user_team or is_unrestricted_company):
|
if not (is_assigned_to_user or is_assigned_to_user_team or is_unrestricted_company or is_team_member_edit_allowed):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
contract = intervention.contract
|
contract = intervention.contract
|
||||||
|
|
@ -682,7 +688,11 @@ def filter_viewable_interventions_for_user(user, limit_to_default_thematic=False
|
||||||
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
|
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
|
||||||
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())
|
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())
|
||||||
|
|
||||||
company_q = Q(assigned_member=company_member) | Q(assigned_team__in=list(company_member.teams.all()))
|
company_q = (
|
||||||
|
Q(assigned_member=company_member) |
|
||||||
|
Q(assigned_team__in=list(company_member.teams.all())) |
|
||||||
|
Q(contract__allow_team_members_edit=True, assigned_member__teams__in=list(company_member.teams.all()))
|
||||||
|
)
|
||||||
if unrestricted_companies:
|
if unrestricted_companies:
|
||||||
company_q |= Q(assigned_member__isnull=True, assigned_team__isnull=True, assigned_provider_id__in=unrestricted_companies)
|
company_q |= Q(assigned_member__isnull=True, assigned_team__isnull=True, assigned_provider_id__in=unrestricted_companies)
|
||||||
else:
|
else:
|
||||||
|
|
@ -725,7 +735,11 @@ def filter_viewable_interventions_for_user(user, limit_to_default_thematic=False
|
||||||
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
|
unrestricted_teams = company_member.teams.filter(restrict_to_assigned_interventions=False)
|
||||||
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())
|
unrestricted_companies = list(unrestricted_teams.values_list('company_id', flat=True).distinct())
|
||||||
|
|
||||||
assign_filter = Q(assigned_member=company_member) | Q(assigned_team__in=list(company_member.teams.all()))
|
assign_filter = (
|
||||||
|
Q(assigned_member=company_member) |
|
||||||
|
Q(assigned_team__in=list(company_member.teams.all())) |
|
||||||
|
Q(contract__allow_team_members_edit=True, assigned_member__teams__in=list(company_member.teams.all()))
|
||||||
|
)
|
||||||
if unrestricted_companies:
|
if unrestricted_companies:
|
||||||
assign_filter |= Q(assigned_provider_id__in=unrestricted_companies)
|
assign_filter |= Q(assigned_provider_id__in=unrestricted_companies)
|
||||||
qs = qs.filter(assign_filter)
|
qs = qs.filter(assign_filter)
|
||||||
|
|
@ -953,6 +967,22 @@ def can_edit_intervention(user, intervention, user_config=None):
|
||||||
assigned_member = getattr(intervention, 'assigned_member', None)
|
assigned_member = getattr(intervention, 'assigned_member', None)
|
||||||
assigned_user_id = getattr(assigned_member, 'user_id', None) if assigned_member else None
|
assigned_user_id = getattr(assigned_member, 'user_id', None) if assigned_member else None
|
||||||
if assigned_user_id is not None and assigned_user_id != user.id:
|
if assigned_user_id is not None and assigned_user_id != user.id:
|
||||||
|
# Vérifier si le contrat autorise la modification par les membres de la même équipe
|
||||||
|
contract = intervention.contract
|
||||||
|
allow_team_edit = bool(contract and getattr(contract, 'allow_team_members_edit', False))
|
||||||
|
is_same_team_member = False
|
||||||
|
if allow_team_edit:
|
||||||
|
company_member = CompanyMember.objects.filter(user=user).prefetch_related('teams').first()
|
||||||
|
if company_member:
|
||||||
|
user_team_ids = set(company_member.teams.values_list('id', flat=True))
|
||||||
|
if intervention.assigned_team_id and intervention.assigned_team_id in user_team_ids:
|
||||||
|
is_same_team_member = True
|
||||||
|
elif assigned_member:
|
||||||
|
assigned_member_team_ids = set(assigned_member.teams.values_list('id', flat=True))
|
||||||
|
if user_team_ids & assigned_member_team_ids:
|
||||||
|
is_same_team_member = True
|
||||||
|
|
||||||
|
if not is_same_team_member:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
contract = intervention.contract
|
contract = intervention.contract
|
||||||
|
|
@ -1054,6 +1084,15 @@ def can_manage_occupations(user, intervention, user_config=None):
|
||||||
and intervention.assigned_team_id in member_team_ids):
|
and intervention.assigned_team_id in member_team_ids):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Si le contrat autorise la modification par les membres d'équipe, autoriser si partage d'équipe
|
||||||
|
contract = intervention.contract
|
||||||
|
if contract and getattr(contract, 'allow_team_members_edit', False):
|
||||||
|
assigned_member = getattr(intervention, 'assigned_member', None)
|
||||||
|
if assigned_member:
|
||||||
|
assigned_member_team_ids = set(assigned_member.teams.values_list('id', flat=True))
|
||||||
|
if set(member_team_ids) & assigned_member_team_ids:
|
||||||
|
return True
|
||||||
|
|
||||||
# Autoriser tous les techniciens qui ont démarré cette intervention, même si elle est ensuite réassignée à une autre équipe ou un autre membre
|
# Autoriser tous les techniciens qui ont démarré cette intervention, même si elle est ensuite réassignée à une autre équipe ou un autre membre
|
||||||
if InterventionTimeLine.objects.filter(
|
if InterventionTimeLine.objects.filter(
|
||||||
intervention=intervention,
|
intervention=intervention,
|
||||||
|
|
|
||||||
|
|
@ -776,7 +776,8 @@ class ExternalManagerScheduleUpdateTests(TestCase):
|
||||||
}
|
}
|
||||||
res = self.client.post(self.update_url, data=json.dumps(payload), content_type='application/json')
|
res = self.client.post(self.update_url, data=json.dumps(payload), content_type='application/json')
|
||||||
self.assertEqual(res.status_code, 403)
|
self.assertEqual(res.status_code, 403)
|
||||||
self.assertIn("entreprise", res.json().get('error', '').lower())
|
err_msg = res.json().get('error', '').lower()
|
||||||
|
self.assertTrue('entreprise' in err_msg or 'company' in err_msg)
|
||||||
|
|
||||||
def test_external_manager_can_unassign_assigned_status_intervention_to_backlog(self):
|
def test_external_manager_can_unassign_assigned_status_intervention_to_backlog(self):
|
||||||
itv_assigned = Intervention.objects.create(
|
itv_assigned = Intervention.objects.create(
|
||||||
|
|
|
||||||
|
|
@ -1670,12 +1670,12 @@ class TrafficlightsReprogrammingTypeTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
self.user = User.objects.create_user(username='admin_tl', password='pwd')
|
self.user = User.objects.create_user(username='admin_tl', password='pwd')
|
||||||
self.role_admin = Role.objects.create(name='admin')
|
self.role_admin, _ = Role.objects.get_or_create(name='admin')
|
||||||
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
||||||
self.user_config.roles.add(self.role_admin)
|
self.user_config.roles.add(self.role_admin)
|
||||||
|
|
||||||
self.tl_thematic = Thematic.objects.create(code='trafficlights', name_fr='Feux tricolores', name_nl='Verkeerslichten')
|
self.tl_thematic, _ = Thematic.objects.get_or_create(code='trafficlights', defaults={'name_fr': 'Feux tricolores', 'name_nl': 'Verkeerslichten'})
|
||||||
self.struct_thematic = Thematic.objects.create(code='structures', name_fr='Ouvrages d\'art', name_nl='Kunstwerken')
|
self.struct_thematic, _ = Thematic.objects.get_or_create(code='structures', defaults={'name_fr': "Ouvrages d'art", 'name_nl': 'Kunstwerken'})
|
||||||
|
|
||||||
UserThematics.objects.create(user_config=self.user_config, thematic=self.tl_thematic, can_edit_interventions=True)
|
UserThematics.objects.create(user_config=self.user_config, thematic=self.tl_thematic, can_edit_interventions=True)
|
||||||
UserThematics.objects.create(user_config=self.user_config, thematic=self.struct_thematic, can_edit_interventions=True)
|
UserThematics.objects.create(user_config=self.user_config, thematic=self.struct_thematic, can_edit_interventions=True)
|
||||||
|
|
|
||||||
|
|
@ -609,7 +609,7 @@ class PreventiveTemplateTeamAssignmentTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
self.thematic = Thematic.objects.create(code='trafficlights', name_fr='Signalisation', name_nl='Signalisatie')
|
self.thematic, _ = Thematic.objects.get_or_create(code='trafficlights', defaults={'name_fr': 'Signalisation', 'name_nl': 'Signalisatie'})
|
||||||
self.company = Company.objects.create(name='Provider Corp')
|
self.company = Company.objects.create(name='Provider Corp')
|
||||||
self.team_a = CompanyTeam.objects.create(name='Equipe Alpha', company=self.company)
|
self.team_a = CompanyTeam.objects.create(name='Equipe Alpha', company=self.company)
|
||||||
self.team_a.thematics.add(self.thematic)
|
self.team_a.thematics.add(self.thematic)
|
||||||
|
|
|
||||||
211
loko/interventions/tests/test_team_members_edit.py
Normal file
211
loko/interventions/tests/test_team_members_edit.py
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
from datetime import timedelta
|
||||||
|
from django.test import TestCase, RequestFactory
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from common.models import Role, UserConfig, Thematic, UserThematics, UserContractAccess, UserContractStatusPermission
|
||||||
|
from contracts.models import Company, CompanyTeam, CompanyMember, Contract
|
||||||
|
from interventions.models import Intervention, STATUS_CHOICES
|
||||||
|
from interventions.permissions import can_edit_intervention, can_manage_occupations, can_view_intervention, filter_viewable_interventions_for_user
|
||||||
|
from interventions.views.workflow import attempt_status_update
|
||||||
|
|
||||||
|
|
||||||
|
class TeamMembersEditTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.User = get_user_model()
|
||||||
|
self.role_tech, _ = Role.objects.get_or_create(name='technician')
|
||||||
|
|
||||||
|
self.company = Company.objects.create(name='Prestataire Test')
|
||||||
|
self.team1 = CompanyTeam.objects.create(name='Équipe Alpha', company=self.company)
|
||||||
|
self.team2 = CompanyTeam.objects.create(name='Équipe Bêta', company=self.company)
|
||||||
|
|
||||||
|
self.thematic = Thematic.objects.create(code='roads', name_fr='Voirie', name_nl='Wegen')
|
||||||
|
|
||||||
|
# Utilisateur Tech A (Membre de l'Équipe Alpha)
|
||||||
|
self.user_a = self.User.objects.create_user(username='tech_a', password='pwd')
|
||||||
|
self.config_a = UserConfig.objects.create(user=self.user_a, is_intern=False)
|
||||||
|
self.config_a.roles.add(self.role_tech)
|
||||||
|
self.member_a = CompanyMember.objects.create(user=self.user_a, name='Technicien A')
|
||||||
|
self.member_a.teams.add(self.team1)
|
||||||
|
|
||||||
|
# Utilisateur Tech B (Collègue dans l'Équipe Alpha)
|
||||||
|
self.user_b = self.User.objects.create_user(username='tech_b', password='pwd')
|
||||||
|
self.config_b = UserConfig.objects.create(user=self.user_b, is_intern=False)
|
||||||
|
self.config_b.roles.add(self.role_tech)
|
||||||
|
self.member_b = CompanyMember.objects.create(user=self.user_b, name='Technicien B')
|
||||||
|
self.member_b.teams.add(self.team1)
|
||||||
|
|
||||||
|
# Utilisateur Tech C (Technicien dans une autre équipe: Bêta)
|
||||||
|
self.user_c = self.User.objects.create_user(username='tech_c', password='pwd')
|
||||||
|
self.config_c = UserConfig.objects.create(user=self.user_c, is_intern=False)
|
||||||
|
self.config_c.roles.add(self.role_tech)
|
||||||
|
self.member_c = CompanyMember.objects.create(user=self.user_c, name='Technicien C')
|
||||||
|
self.member_c.teams.add(self.team2)
|
||||||
|
|
||||||
|
# Contrat avec allow_team_members_edit=False
|
||||||
|
self.contract_no_team_edit = Contract.objects.create(
|
||||||
|
company=self.company,
|
||||||
|
contract_number='C-NO-TEAM-EDIT',
|
||||||
|
start_date=timezone.now().date() - timedelta(days=10),
|
||||||
|
end_date=timezone.now().date() + timedelta(days=10),
|
||||||
|
is_active=True,
|
||||||
|
allow_team_members_edit=False,
|
||||||
|
)
|
||||||
|
self.contract_no_team_edit.thematics.add(self.thematic)
|
||||||
|
|
||||||
|
# Contrat avec allow_team_members_edit=True
|
||||||
|
self.contract_with_team_edit = Contract.objects.create(
|
||||||
|
company=self.company,
|
||||||
|
contract_number='C-WITH-TEAM-EDIT',
|
||||||
|
start_date=timezone.now().date() - timedelta(days=10),
|
||||||
|
end_date=timezone.now().date() + timedelta(days=10),
|
||||||
|
is_active=True,
|
||||||
|
allow_team_members_edit=True,
|
||||||
|
)
|
||||||
|
self.contract_with_team_edit.thematics.add(self.thematic)
|
||||||
|
|
||||||
|
# Permissions thématiques et contrats pour les 3 techniciens
|
||||||
|
for cfg in [self.config_a, self.config_b, self.config_c]:
|
||||||
|
UserThematics.objects.create(
|
||||||
|
user_config=cfg,
|
||||||
|
thematic=self.thematic,
|
||||||
|
can_view_interventions=True,
|
||||||
|
can_edit_interventions=True,
|
||||||
|
)
|
||||||
|
for contract in [self.contract_no_team_edit, self.contract_with_team_edit]:
|
||||||
|
uca = UserContractAccess.objects.create(
|
||||||
|
user_config=cfg,
|
||||||
|
contract=contract,
|
||||||
|
can_view_interventions=True,
|
||||||
|
)
|
||||||
|
for status in ['assigned', 'in_progress', 'on_pause', 'finished', 'to_be_processed']:
|
||||||
|
UserContractStatusPermission.objects.create(
|
||||||
|
user_contract=uca,
|
||||||
|
status=status,
|
||||||
|
can_view=True,
|
||||||
|
can_edit=True,
|
||||||
|
can_change_status_to=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_default_behavior_team_member_cannot_edit_assigned_intervention(self):
|
||||||
|
"""Si allow_team_members_edit=False, Tech B ne peut pas modifier l'intervention assignée à Tech A."""
|
||||||
|
intervention = Intervention.objects.create(
|
||||||
|
title='Intervention Test 1',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_no_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='assigned',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tech A peut modifier
|
||||||
|
self.assertTrue(can_edit_intervention(self.user_a, intervention))
|
||||||
|
|
||||||
|
# Tech B (même équipe) ne peut PAS modifier
|
||||||
|
self.assertFalse(can_edit_intervention(self.user_b, intervention))
|
||||||
|
|
||||||
|
# Tentative de passage à 'in_progress' par Tech B
|
||||||
|
factory = RequestFactory()
|
||||||
|
request = factory.post(f'/interventions/{intervention.id}/update_status/', {'new_status': 'in_progress'})
|
||||||
|
request.user = self.user_b
|
||||||
|
|
||||||
|
success, msg, _ = attempt_status_update(request, intervention, self.user_b, 'in_progress')
|
||||||
|
self.assertFalse(success)
|
||||||
|
self.assertEqual(intervention.status, 'assigned')
|
||||||
|
|
||||||
|
def test_allow_team_members_edit_true_allows_colleague_to_edit_and_transition(self):
|
||||||
|
"""Si allow_team_members_edit=True, Tech B (même équipe) peut modifier et faire avancer l'intervention."""
|
||||||
|
intervention = Intervention.objects.create(
|
||||||
|
title='Intervention Test 2',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_with_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='assigned',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tech A peut modifier
|
||||||
|
self.assertTrue(can_edit_intervention(self.user_a, intervention))
|
||||||
|
|
||||||
|
# Tech B (même équipe) PEUT modifier
|
||||||
|
self.assertTrue(can_edit_intervention(self.user_b, intervention))
|
||||||
|
|
||||||
|
# Tech B peut passer au statut 'in_progress'
|
||||||
|
factory = RequestFactory()
|
||||||
|
request = factory.post(f'/interventions/{intervention.id}/update_status/', {'new_status': 'in_progress'})
|
||||||
|
request.user = self.user_b
|
||||||
|
|
||||||
|
success, msg, _ = attempt_status_update(request, intervention, self.user_b, 'in_progress')
|
||||||
|
self.assertTrue(success)
|
||||||
|
intervention.refresh_from_db()
|
||||||
|
self.assertEqual(intervention.status, 'in_progress')
|
||||||
|
|
||||||
|
# Tech B peut toujours éditer lorsque le statut est 'in_progress', 'on_pause', 'finished'
|
||||||
|
self.assertTrue(can_edit_intervention(self.user_b, intervention))
|
||||||
|
|
||||||
|
# Tech B peut mettre en pause
|
||||||
|
success, msg, _ = attempt_status_update(request, intervention, self.user_b, 'on_pause', pause_reason='order_material')
|
||||||
|
self.assertTrue(success)
|
||||||
|
intervention.refresh_from_db()
|
||||||
|
self.assertEqual(intervention.status, 'on_pause')
|
||||||
|
self.assertTrue(can_edit_intervention(self.user_b, intervention))
|
||||||
|
|
||||||
|
def test_allow_team_members_edit_true_rejects_unrelated_technician(self):
|
||||||
|
"""Même si allow_team_members_edit=True, un technicien d'une autre équipe (Tech C) ne peut pas modifier."""
|
||||||
|
intervention = Intervention.objects.create(
|
||||||
|
title='Intervention Test 3',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_with_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='assigned',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tech C (Équipe Bêta, membre_a est dans Équipe Alpha) ne peut PAS modifier
|
||||||
|
self.assertFalse(can_edit_intervention(self.user_c, intervention))
|
||||||
|
|
||||||
|
def test_can_manage_occupations_for_team_colleague(self):
|
||||||
|
"""Vérifie la gestion des occupations entre collègues d'une même équipe selon le paramètre du contrat."""
|
||||||
|
# Cas 1 : allow_team_members_edit=False et pas de assigned_team
|
||||||
|
itv1 = Intervention.objects.create(
|
||||||
|
title='Intervention Occupations 1',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_no_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='in_progress',
|
||||||
|
)
|
||||||
|
self.assertTrue(can_manage_occupations(self.user_a, itv1))
|
||||||
|
self.assertFalse(can_manage_occupations(self.user_b, itv1))
|
||||||
|
|
||||||
|
# Cas 2 : allow_team_members_edit=True
|
||||||
|
itv2 = Intervention.objects.create(
|
||||||
|
title='Intervention Occupations 2',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_with_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='in_progress',
|
||||||
|
)
|
||||||
|
self.assertTrue(can_manage_occupations(self.user_a, itv2))
|
||||||
|
self.assertTrue(can_manage_occupations(self.user_b, itv2))
|
||||||
|
self.assertFalse(can_manage_occupations(self.user_c, itv2))
|
||||||
|
|
||||||
|
def test_visibility_with_restrict_to_assigned_interventions(self):
|
||||||
|
"""Vérifie que la restriction aux interventions assignées permet la visibilité aux collègues si allow_team_members_edit=True."""
|
||||||
|
self.team1.restrict_to_assigned_interventions = True
|
||||||
|
self.team1.save()
|
||||||
|
|
||||||
|
# Intervention sur contrat avec allow_team_members_edit=True
|
||||||
|
itv = Intervention.objects.create(
|
||||||
|
title='Intervention Visibilité',
|
||||||
|
thematic=self.thematic,
|
||||||
|
contract=self.contract_with_team_edit,
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=self.member_a,
|
||||||
|
status='assigned',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(can_view_intervention(self.user_a, itv))
|
||||||
|
self.assertTrue(can_view_intervention(self.user_b, itv))
|
||||||
|
self.assertIn(itv, filter_viewable_interventions_for_user(self.user_b))
|
||||||
Loading…
Reference in a new issue