diff --git a/streetup/assets/models/parking.py b/streetup/assets/models/parking.py index a0004ee..fd65233 100644 --- a/streetup/assets/models/parking.py +++ b/streetup/assets/models/parking.py @@ -133,7 +133,7 @@ class ParkingSpot(AbstractGeoAsset): ordering = ['code', 'name_fr'] def get_absolute_url(self): - return reverse('assets:sign_assets_detail', kwargs={'asset_model': 'parking_spot', 'asset_id': self.id}) + return reverse('assets:parking_assets_detail', kwargs={'asset_model': 'parking_spot', 'asset_id': self.id}) def __str__(self): type_label = self.get_spot_type_display() diff --git a/streetup/assets/templates/assets/components/asset_interventions.html b/streetup/assets/templates/assets/components/asset_interventions.html index 06e6fdf..59e8772 100644 --- a/streetup/assets/templates/assets/components/asset_interventions.html +++ b/streetup/assets/templates/assets/components/asset_interventions.html @@ -9,6 +9,11 @@ {{ interventions|length }} {% endif %} + {% if can_add_intervention %} + + {% endif %}
{% if interventions %} @@ -37,6 +42,13 @@ {{ intervention.title }} + {% if intervention.maintain_type == 'corrective' %} + {% translate "Corrective" %} + {% elif intervention.maintain_type == 'preventive' %} + {% translate "Préventive" %} + {% elif intervention.maintain_type == 'ameliorative' %} + {% translate "Améliorative" %} + {% endif %} {% if intervention.symptom %} @@ -87,3 +99,135 @@ {% endif %}
+ +{% if can_add_intervention %} + + + + +{% endif %} diff --git a/streetup/assets/templatetags/asset_tags.py b/streetup/assets/templatetags/asset_tags.py index 5064a06..64cdb48 100644 --- a/streetup/assets/templatetags/asset_tags.py +++ b/streetup/assets/templatetags/asset_tags.py @@ -438,19 +438,23 @@ def warranty_remaining_months(asset): return remaining -@register.inclusion_tag('assets/components/asset_interventions.html') -def asset_interventions(asset): +@register.inclusion_tag('assets/components/asset_interventions.html', takes_context=True) +def asset_interventions(context, asset): """ Display all interventions associated with an asset. + Also provides context for adding a new intervention directly from the asset page. """ if asset is None or not hasattr(asset, 'pk') or asset.pk is None: return { 'asset': asset, 'interventions': [], + 'can_add_intervention': False, + 'symptoms': [], } from django.contrib.contenttypes.models import ContentType - from interventions.models import Intervention, InterventionAsset, STATUS_COLORS, STATUS_COLORS_DARK_TEXT + from interventions.models import Intervention, InterventionAsset, Symptom, STATUS_COLORS, STATUS_COLORS_DARK_TEXT + from interventions.permissions import can_add_intervention asset_ct = ContentType.objects.get_for_model(asset) intervention_ids = InterventionAsset.objects.filter( @@ -464,10 +468,31 @@ def asset_interventions(asset): 'symptom', 'contract', 'assigned_provider', 'assigned_team', 'created_by' ).order_by('-creation_time')) + thematic = None + if hasattr(asset, 'category') and asset.category and asset.category.thematic: + thematic = asset.category.thematic + + request = context.get('request') + user = request.user if request else None + can_add = False + symptoms = [] + + if user and user.is_authenticated: + can_add = can_add_intervention(user, thematic) + if can_add: + if thematic: + symptoms = list(Symptom.objects.filter(thematic=thematic, is_active=True).order_by('order_number', 'name_fr')) + else: + symptoms = list(Symptom.objects.filter(is_active=True).order_by('name_fr')) + return { 'asset': asset, + 'asset_model': asset_ct.model, 'interventions': interventions, + 'can_add_intervention': can_add, + 'symptoms': symptoms, 'status_colors': STATUS_COLORS, 'status_colors_dark_text': STATUS_COLORS_DARK_TEXT, } + diff --git a/streetup/assets/urls.py b/streetup/assets/urls.py index 631d153..779376e 100644 --- a/streetup/assets/urls.py +++ b/streetup/assets/urls.py @@ -42,6 +42,7 @@ urlpatterns = [ path('//update/', asset_editing.update_asset, name='update_asset'), path('//archive/', asset_editing.archive_asset, name='archive_asset'), path('//replace/', asset_editing.replace_asset, name='replace_asset'), + path('//create-intervention/', asset_editing.create_intervention_for_asset, name='create_intervention_for_asset'), path('//documents/add/', asset_editing.asset_add_document, name='asset_add_document'), path('//documents//delete/', asset_editing.asset_delete_document, name='asset_delete_document'), path('//documents/', asset_editing.asset_document_list_html, name='asset_document_list_html'), diff --git a/streetup/assets/views/asset_editing.py b/streetup/assets/views/asset_editing.py index 5573d3e..647abec 100644 --- a/streetup/assets/views/asset_editing.py +++ b/streetup/assets/views/asset_editing.py @@ -2376,3 +2376,84 @@ def bulk_edit_assets(request): } }) + +@require_POST +def create_intervention_for_asset(request, asset_model, asset_id): + """ + Create a new intervention directly linked to an asset. + Requires user to have intervention creation permission for the asset's thematic. + """ + if not request.user.is_authenticated: + return JsonResponse({'success': False, 'error': _("Non authentifié.")}, status=401) + + asset, model_class, content_type = _get_asset_by_ct(asset_model, asset_id) + + thematic = None + if hasattr(asset, 'category') and asset.category and asset.category.thematic: + thematic = asset.category.thematic + + from interventions.permissions import can_add_intervention + if not can_add_intervention(request.user, thematic): + return JsonResponse({'success': False, 'error': _("Permission refusée pour créer une intervention.")}, status=403) + + title = request.POST.get('title', '').strip() + if not title: + return JsonResponse({'success': False, 'error': _("Le titre est obligatoire.")}, status=400) + + symptom_id = request.POST.get('symptom_id') + maintain_type = request.POST.get('maintain_type', 'corrective') + priority = request.POST.get('priority', '3') + init_description = request.POST.get('init_description', '').strip() + + from interventions.models import Intervention, InterventionAsset, Symptom, InterventionLocation + + symptom = None + if symptom_id: + symptom = Symptom.objects.filter(pk=symptom_id, is_active=True).first() + + geom = getattr(asset, 'geom', None) + if geom is not None: + from django.contrib.gis.geos import Polygon, MultiPolygon + if isinstance(geom, Polygon): + geom = MultiPolygon(geom) + elif not isinstance(geom, MultiPolygon): + geom = None + + try: + with transaction.atomic(): + intervention = Intervention.objects.create( + title=title, + thematic=thematic, + symptom=symptom, + maintain_type=maintain_type, + priority=priority, + init_description=init_description, + status='in_preparation', + created_by=request.user, + geom=geom, + ) + InterventionAsset.objects.create( + intervention=intervention, + content_type=content_type, + object_id=asset.pk + ) + loc_obj = getattr(asset, 'street', None) or getattr(asset, 'location', None) or getattr(asset, 'intersection', None) + if loc_obj: + loc_ct = ContentType.objects.get_for_model(loc_obj) + InterventionLocation.objects.get_or_create( + intervention=intervention, + content_type=loc_ct, + object_id=loc_obj.pk + ) + + redirect_url = reverse('interventions:intervention_detail', args=[intervention.id]) + return JsonResponse({ + 'success': True, + 'redirect_url': redirect_url, + 'intervention_id': intervention.id, + 'intervention_code': intervention.code, + }) + except Exception as e: + return JsonResponse({'success': False, 'error': str(e)}, status=500) + + diff --git a/streetup/assets/views/core.py b/streetup/assets/views/core.py index 624d420..372901a 100644 --- a/streetup/assets/views/core.py +++ b/streetup/assets/views/core.py @@ -244,10 +244,11 @@ def locations_geojson(request): def mobile_asset_detail(request, thematic_code, asset_model, asset_id): """Mobile-friendly asset detail using the generic template with the mobile base.""" try: - content_type = ContentType.objects.get(app_label='assets', model=asset_model) + model_name = asset_model.lower().replace('_', '') + content_type = ContentType.objects.get(app_label='assets', model=model_name) model_class = content_type.model_class() except ContentType.DoesNotExist: - raise Http404(_("Modèle %(asset_model)s inconnu.") % (asset_model)) + raise Http404(_("Modèle %(asset_model)s inconnu.") % {'asset_model': asset_model}) asset = get_object_or_404(model_class, id=asset_id) diff --git a/streetup/assets/views/sign.py b/streetup/assets/views/sign.py index ce9512c..84fc228 100644 --- a/streetup/assets/views/sign.py +++ b/streetup/assets/views/sign.py @@ -775,10 +775,11 @@ def sign_streets_detail(request, street_id): def sign_assets_detail(request, asset_model, asset_id): try: # Récupérer le ContentType pour le modèle fourni (spécifier l'app_label 'assets' pour éviter les conflits) - content_type = ContentType.objects.get(app_label='assets', model=asset_model) + model_name = asset_model.lower().replace('_', '') + content_type = ContentType.objects.get(app_label='assets', model=model_name) model_class = content_type.model_class() except ContentType.DoesNotExist: - raise Http404(_("Modèle %(asset_model)s inconnu.") % (asset_model)) + raise Http404(_("Modèle %(asset_model)s inconnu.") % {'asset_model': asset_model}) # Récupérer l’objet asset asset = get_object_or_404(model_class, id=asset_id) diff --git a/streetup/assets/views/structures.py b/streetup/assets/views/structures.py index b6764ba..cc02bdf 100644 --- a/streetup/assets/views/structures.py +++ b/streetup/assets/views/structures.py @@ -647,10 +647,11 @@ def structures_locations_detail(request, location_id): def structures_assets_detail(request, asset_model, asset_id): try: # Récupérer le ContentType pour le modèle fourni - content_type = ContentType.objects.get(model=asset_model) + model_name = asset_model.lower().replace('_', '') + content_type = ContentType.objects.get(app_label='assets', model=model_name) model_class = content_type.model_class() except ContentType.DoesNotExist: - raise Http404(_("Modèle %(asset_model)s inconnu.") % (asset_model)) + raise Http404(_("Modèle %(asset_model)s inconnu.") % {'asset_model': asset_model}) # Récupérer l’objet asset asset = get_object_or_404(model_class, id=asset_id)