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.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,10 +239,10 @@ 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])
)
# Providers and contracts (intern users only)
provider_choices = []
contract_choices = []
if user_config.is_intern:
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:
@ -255,9 +255,10 @@ def index(request):
contracts_qs = contracts_qs.filter(thematics__code__in=authorized_thematic_codes)
contracts_qs = contracts_qs.filter(id__in=accessible_contract_ids).distinct()
provider_choices = []
contract_choices = []
if user_config.is_intern:
# Build contract choices with thematic codes
from django.contrib.auth import get_user_model
User = get_user_model()
for contract in contracts_qs:
thematic_codes = list(contract.thematics.values_list('code', flat=True))
managers_qs = User.objects.filter(
@ -359,18 +360,24 @@ def index(request):
# Build role-persons data (Gestionnaire / Contrôleur / Inspecteur)
# 1. Managers
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)
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():
managed_itv_map[itv_data['intervention_manager_id']].add(itv_data['contract_id'])
manager_users_qs = (
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')
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 = (
User.objects.filter(manager_q)
.prefetch_related('companymember_set__teams', 'config__company')
.distinct()
.order_by('first_name', 'last_name', 'username')