fix(interventions): resolve UnboundLocalError contracts_qs for external managers

This commit is contained in:
kdeterme 2026-10-01 09:29:54 +02:00
parent 927c049d02
commit 8df84704dc
2 changed files with 99 additions and 21 deletions

View file

@ -2195,6 +2195,77 @@ class InterventionRolePersonFilteringTests(TestCase):
self.assertIn(self.itv_type_inspection.id, itv_ids_insp) self.assertIn(self.itv_type_inspection.id, itv_ids_insp)
self.assertNotIn(self.itv_controller.id, itv_ids_insp) self.assertNotIn(self.itv_controller.id, itv_ids_insp)
def test_interventions_index_external_manager(self):
"""Verify that an external manager (is_intern=False) can access interventions index without 500 error."""
ext_manager = self.User.objects.create_user(
username='ext_manager_test_user',
password='pwd',
first_name='Eric',
last_name='Gestionnaire'
)
role_ext_mgr, _ = Role.objects.get_or_create(name='external_manager')
ext_config = UserConfig.objects.create(user=ext_manager, is_intern=False)
ext_config.roles.add(role_ext_mgr)
UserThematics.objects.create(
user_config=ext_config,
thematic=self.thematic,
can_view_interventions=True,
can_edit_interventions=True,
)
# Create a contract accessible to this external manager
from datetime import date, timedelta
contract = Contract.objects.create(
contract_number='CONT-EXT-01',
company=self.company,
start_date=date.today(),
end_date=date.today() + timedelta(days=365),
uses_intervention_manager=True
)
contract.thematics.add(self.thematic)
from common.models import UserContractAccess
UserContractAccess.objects.create(
user_config=ext_config,
contract=contract,
can_view_interventions=True,
can_approve=True,
)
self.client.login(username='ext_manager_test_user', password='pwd')
url = reverse('interventions:interventions_index')
resp = self.client.get(url, follow=True)
self.assertEqual(resp.status_code, 200)
self.assertIn('role_persons_data', resp.context)
role_data = resp.context['role_persons_data']
self.assertIn('manager', role_data)
self.assertIn('controller', role_data)
self.assertIn('inspector', role_data)
manager_ids = [m['id'] for m in role_data['manager']]
self.assertIn(ext_manager.id, manager_ids)
def test_interventions_index_non_intern_user_without_contracts(self):
"""Verify that a non-intern user without any contracts can access interventions index without 500 error."""
ext_user = self.User.objects.create_user(
username='ext_simple_user',
password='pwd'
)
role_technician, _ = Role.objects.get_or_create(name='technician')
ext_config = UserConfig.objects.create(user=ext_user, is_intern=False)
ext_config.roles.add(role_technician)
UserThematics.objects.create(
user_config=ext_config,
thematic=self.thematic,
can_view_interventions=True,
can_edit_interventions=False,
)
self.client.login(username='ext_simple_user', password='pwd')
url = reverse('interventions:interventions_index')
resp = self.client.get(url, follow=True)
self.assertEqual(resp.status_code, 200)
self.assertIn('role_persons_data', resp.context)

View file

@ -239,25 +239,26 @@ def index(request):
key=lambda t: (t[3] if t[3] is not None else t[0], 1 if t[3] is not None else 0, t[1]) key=lambda t: (t[3] if t[3] is not None else t[0], 1 if t[3] is not None else 0, t[1])
) )
# Providers and contracts (intern users only) from django.contrib.auth import get_user_model
User = get_user_model()
# Providers and contracts
is_admin = user_config.roles.filter(name='admin').exists() or request.user.is_superuser
contracts_qs = Contract.objects.select_related('company').prefetch_related('thematics')
if not is_admin:
accessible_contract_ids = UserContractAccess.objects.filter(
user_config=user_config,
can_view_interventions=True,
).values_list('contract_id', flat=True)
if authorized_thematic_codes:
contracts_qs = contracts_qs.filter(thematics__code__in=authorized_thematic_codes)
contracts_qs = contracts_qs.filter(id__in=accessible_contract_ids).distinct()
provider_choices = [] provider_choices = []
contract_choices = [] contract_choices = []
if user_config.is_intern: if user_config.is_intern:
is_admin = user_config.roles.filter(name='admin').exists() or request.user.is_superuser
contracts_qs = Contract.objects.select_related('company').prefetch_related('thematics')
if not is_admin:
accessible_contract_ids = UserContractAccess.objects.filter(
user_config=user_config,
can_view_interventions=True,
).values_list('contract_id', flat=True)
if authorized_thematic_codes:
contracts_qs = contracts_qs.filter(thematics__code__in=authorized_thematic_codes)
contracts_qs = contracts_qs.filter(id__in=accessible_contract_ids).distinct()
# Build contract choices with thematic codes # Build contract choices with thematic codes
from django.contrib.auth import get_user_model
User = get_user_model()
for contract in contracts_qs: for contract in contracts_qs:
thematic_codes = list(contract.thematics.values_list('code', flat=True)) thematic_codes = list(contract.thematics.values_list('code', flat=True))
managers_qs = User.objects.filter( managers_qs = User.objects.filter(
@ -359,18 +360,24 @@ def index(request):
# Build role-persons data (Gestionnaire / Contrôleur / Inspecteur) # Build role-persons data (Gestionnaire / Contrôleur / Inspecteur)
# 1. Managers # 1. Managers
user_access_map = defaultdict(set) user_access_map = defaultdict(set)
for uca in UserContractAccess.objects.filter(contract__in=contracts_qs, can_approve=True): for uca in UserContractAccess.objects.filter(
Q(contract__in=contracts_qs) & (Q(can_approve=True) | Q(user_config__roles__name__in=['manager', 'external_manager']))
):
user_access_map[uca.user_config.user_id].add(uca.contract_id) user_access_map[uca.user_config.user_id].add(uca.contract_id)
managed_itv_map = defaultdict(set) managed_itv_map = defaultdict(set)
for itv_data in Intervention.objects.filter(contract__in=contracts_qs, intervention_manager__isnull=False).values('intervention_manager_id', 'contract_id').distinct(): for itv_data in Intervention.objects.filter(contract__in=contracts_qs, intervention_manager__isnull=False).values('intervention_manager_id', 'contract_id').distinct():
managed_itv_map[itv_data['intervention_manager_id']].add(itv_data['contract_id']) managed_itv_map[itv_data['intervention_manager_id']].add(itv_data['contract_id'])
manager_q = (
Q(config__usercontractaccess__contract__in=contracts_qs, config__usercontractaccess__can_approve=True) |
Q(config__usercontractaccess__contract__in=contracts_qs, config__roles__name__in=['manager', 'external_manager']) |
Q(managed_interventions__contract__in=contracts_qs)
)
if user_config.is_intern:
manager_q |= Q(config__roles__name='manager')
manager_users_qs = ( manager_users_qs = (
User.objects.filter( User.objects.filter(manager_q)
Q(config__usercontractaccess__contract__in=contracts_qs, config__usercontractaccess__can_approve=True) |
Q(managed_interventions__contract__in=contracts_qs) |
Q(config__roles__name='manager')
)
.prefetch_related('companymember_set__teams', 'config__company') .prefetch_related('companymember_set__teams', 'config__company')
.distinct() .distinct()
.order_by('first_name', 'last_name', 'username') .order_by('first_name', 'last_name', 'username')