diff --git a/loko/interventions/tests/test_general.py b/loko/interventions/tests/test_general.py index 0687b9d..3cc46fa 100644 --- a/loko/interventions/tests/test_general.py +++ b/loko/interventions/tests/test_general.py @@ -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) + + diff --git a/loko/interventions/views/general.py b/loko/interventions/views/general.py index ee639c4..8a3623d 100644 --- a/loko/interventions/views/general.py +++ b/loko/interventions/views/general.py @@ -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]) ) - # 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 = [] contract_choices = [] 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 - 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_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 = ( - User.objects.filter( - Q(config__usercontractaccess__contract__in=contracts_qs, config__usercontractaccess__can_approve=True) | - Q(managed_interventions__contract__in=contracts_qs) | - Q(config__roles__name='manager') - ) + User.objects.filter(manager_q) .prefetch_related('companymember_set__teams', 'config__company') .distinct() .order_by('first_name', 'last_name', 'username')