diff --git a/streetup/assets/templates/assets/inspections_list.html b/streetup/assets/templates/assets/inspections_list.html
index 0f6aef6..dbb47b0 100644
--- a/streetup/assets/templates/assets/inspections_list.html
+++ b/streetup/assets/templates/assets/inspections_list.html
@@ -44,7 +44,7 @@
diff --git a/streetup/assets/tests_inspections_views.py b/streetup/assets/tests_inspections_views.py
index da18b44..9642207 100644
--- a/streetup/assets/tests_inspections_views.py
+++ b/streetup/assets/tests_inspections_views.py
@@ -18,6 +18,8 @@ class InspectionViewsTestCase(TestCase):
first_name='Jean',
last_name='Dupont'
)
+ from common.models import UserConfig
+ UserConfig.objects.get_or_create(user=self.user)
self.client = Client()
self.client.login(username='test_inspector', password='password123')
@@ -167,3 +169,41 @@ class InspectionViewsTestCase(TestCase):
self.assertEqual(data['type'], 'FeatureCollection')
self.assertEqual(data['features'][0]['properties']['inspections'][0]['id'], insp.id)
self.assertEqual(data['features'][0]['properties']['inspections_count'], 1)
+
+ def test_inspections_list_search_by_asset_code(self):
+ self.spot.code = "SPOT-TEST-SEARCH-99"
+ self.spot.save()
+
+ insp = AssetInspection.objects.create(
+ asset_content_type=self.spot_ct,
+ asset_object_id=self.spot.id,
+ inspector=self.user,
+ result_status='compliant',
+ nature='visual'
+ )
+
+ response = self.client.get(reverse('assets:inspections_list') + '?tab=all&q=SEARCH-99')
+ self.assertEqual(response.status_code, 200)
+ self.assertIn(insp, response.context['inspections'])
+
+ def test_inspections_list_filter_thematics_uses_can_view_assets(self):
+ from common.models import UserConfig, UserThematics, Thematic
+ t_parking = Thematic.objects.get(code='parking')
+ t_lighting, _ = Thematic.objects.get_or_create(code='publiclighting', defaults={'name_fr': 'Éclairage public'})
+
+ user_config, _ = UserConfig.objects.get_or_create(user=self.user)
+ # L'utilisateur a les droits de voir les assets parking mais pas d'inspecter, et pas de voir l'éclairage public
+ UserThematics.objects.create(user_config=user_config, thematic=t_parking, can_view_assets=True, can_inspect=False)
+ UserThematics.objects.create(user_config=user_config, thematic=t_lighting, can_view_assets=False, can_inspect=False)
+
+ resp = self.client.get(reverse('assets:inspections_list'))
+ self.assertEqual(resp.status_code, 200)
+ self.assertIn(t_parking, resp.context['thematics'])
+ self.assertNotIn(t_lighting, resp.context['thematics'])
+ self.assertFalse(resp.context['can_inspect'])
+
+ def test_user_without_config_returns_none(self):
+ from assets.views.inspections import get_user_viewable_thematics, get_user_inspectable_thematics
+ user_no_config = User.objects.create_user(username='no_config_user', password='pwd')
+ self.assertIsNone(get_user_viewable_thematics(user_no_config))
+ self.assertIsNone(get_user_inspectable_thematics(user_no_config))
diff --git a/streetup/assets/views/inspections.py b/streetup/assets/views/inspections.py
index 4d8428c..a02dbc8 100644
--- a/streetup/assets/views/inspections.py
+++ b/streetup/assets/views/inspections.py
@@ -18,10 +18,21 @@ from assets.models.inspection import (
def get_user_inspectable_thematics(user):
"""
Retourne les thématiques pour lesquelles l'utilisateur détient la permission can_inspect.
+ Si l'utilisateur n'a pas de UserConfig, retourne None.
"""
- if hasattr(user, 'config'):
+ if hasattr(user, 'config') and user.config:
return user.config.get_inspectable_thematics().order_by('name_fr')
- return Thematic.objects.all().order_by('name_fr')
+ return None
+
+
+def get_user_viewable_thematics(user):
+ """
+ Retourne les thématiques pour lesquelles l'utilisateur détient la permission de visualisation (can_view_assets).
+ Si l'utilisateur n'a pas de UserConfig, retourne None.
+ """
+ if hasattr(user, 'config') and user.config:
+ return user.config.get_viewable_assets_thematics().order_by('name_fr')
+ return None
@login_required
@@ -56,15 +67,41 @@ def inspections_list(request):
qs = qs.filter(nature=nature_filter)
if search_q:
- qs = qs.filter(
+ search_filter = (
Q(notes__icontains=search_q) |
Q(inspector__first_name__icontains=search_q) |
Q(inspector__last_name__icontains=search_q) |
Q(inspector__username__icontains=search_q)
)
- thematics = get_user_inspectable_thematics(request.user)
- can_inspect = thematics.exists()
+ asset_q_conditions = Q()
+ for ct in ContentType.objects.filter(app_label='assets'):
+ ModelClass = ct.model_class()
+ if not ModelClass:
+ continue
+
+ model_fields = [f.name for f in ModelClass._meta.get_fields()]
+ asset_search = Q()
+ if 'code' in model_fields:
+ asset_search |= Q(code__icontains=search_q)
+ if 'name' in model_fields:
+ asset_search |= Q(name__icontains=search_q)
+ if 'name_fr' in model_fields:
+ asset_search |= Q(name_fr__icontains=search_q)
+
+ if asset_search:
+ matching_ids = list(ModelClass.objects.filter(asset_search).values_list('pk', flat=True)[:500])
+ if matching_ids:
+ asset_q_conditions |= Q(asset_content_type=ct, asset_object_id__in=matching_ids)
+
+ if asset_q_conditions:
+ search_filter |= asset_q_conditions
+
+ qs = qs.filter(search_filter)
+
+ thematics = get_user_viewable_thematics(request.user)
+ insp_thematics = get_user_inspectable_thematics(request.user)
+ can_inspect = bool(insp_thematics and insp_thematics.exists())
is_mobile = request.path.startswith('/mobile/')
base_template = "mobile/mobile_base.html" if is_mobile else "base.html"
@@ -93,7 +130,7 @@ def quick_inspection_map(request):
Vue cartographique d'inspection rapide optimisée mobile & GPS.
"""
thematics = get_user_inspectable_thematics(request.user)
- if not thematics.exists():
+ if not thematics or not thematics.exists():
return HttpResponseForbidden(_("Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections."))
categories = AssetCategory.objects.filter(thematic__in=thematics).select_related('thematic').order_by('name_fr')
diff --git a/streetup/common/models.py b/streetup/common/models.py
index 9fed840..2f082a2 100644
--- a/streetup/common/models.py
+++ b/streetup/common/models.py
@@ -447,6 +447,20 @@ class UserConfig(models.Model):
return True
return self.userwarehouses.filter(can_administer=True).exists()
+ def get_viewable_assets_thematics(self):
+ """Retourne le QuerySet des thématiques pour lesquelles l'utilisateur est autorisé à voir les assets."""
+ from common.models import Thematic
+ if self.user.is_superuser or self.has_role('admin'):
+ return Thematic.objects.all()
+
+ if not self.userthematics.exists():
+ return None
+
+ return Thematic.objects.filter(
+ userthematics__user_config=self,
+ userthematics__can_view_assets=True
+ ).distinct()
+
def get_inspectable_thematics(self):
"""Retourne le QuerySet des thématiques pour lesquelles l'utilisateur est autorisé à réaliser des inspections."""
from common.models import Thematic