feat: refactor inspection features into a dedicated inspections app and migrate existing models and views.

This commit is contained in:
kdeterme 2026-08-08 15:08:04 +02:00
parent 952f3111d6
commit 757f19ecce
81 changed files with 1090 additions and 1609 deletions

View file

@ -683,7 +683,7 @@ class OutdoorAssetAdmin(admin.GISModelAdmin):
admin.site.register(OutdoorLocationDocument, AssetAdmin) admin.site.register(OutdoorLocationDocument, AssetAdmin)
from .models import ParkingLocation, ParkingSpot, AssetInspection, ParkingSpotInspection from .models import ParkingLocation, ParkingSpot
@admin.register(ParkingLocation) @admin.register(ParkingLocation)
class ParkingLocationAdmin(AssetGISAdmin): class ParkingLocationAdmin(AssetGISAdmin):
@ -703,26 +703,4 @@ class ParkingSpotAdmin(AssetGISAdmin):
) )
search_fields = ("code", "name_fr", "name_nl", "note") search_fields = ("code", "name_fr", "name_nl", "note")
filter_horizontal = ("poles", "panels") filter_horizontal = ("poles", "panels")
raw_id_fields = ("location", "street") raw_id_fields = ("location", "street")
@admin.register(AssetInspection)
class AssetInspectionAdmin(admin.ModelAdmin):
list_display = (
"id", "asset", "inspector", "inspection_date",
"result_status", "apply_changes_to_asset"
)
list_filter = ("result_status", "apply_changes_to_asset", "inspection_date")
search_fields = ("notes", "inspector__username", "inspector__first_name", "inspector__last_name")
raw_id_fields = ("inspector",)
@admin.register(ParkingSpotInspection)
class ParkingSpotInspectionAdmin(admin.ModelAdmin):
list_display = (
"id", "asset", "inspector", "inspection_date",
"result_status", "verified_standard_stand_count",
"verified_cargo_stand_count", "verified_has_scooter_drop_zone",
"apply_changes_to_asset"
)
list_filter = ("result_status", "apply_changes_to_asset", "inspection_date")
search_fields = ("notes", "inspector__username", "inspector__first_name", "inspector__last_name")
raw_id_fields = ("inspector",)

View file

@ -17,8 +17,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
# #: streetup/assets/models/buildings.py:22
#: assets/models/buildings.py:22 #: assets/models/buildings.py:22
# #: streetup/assets/models/buildings.py:22
msgid "Administratif" msgid "Administratif"
msgstr "Administrative" msgstr "Administrative"
@ -902,8 +902,7 @@ msgstr "Planned"
msgid "Actif" msgid "Actif"
msgstr "Active" msgstr "Active"
#: assets/models/core.py:236 assets/models/inspection.py:15 #: assets/models/core.py:236
#: assets/templates/assets/components/inspection_form_modal.html:64
#: assets/templates/assets/detail_generic.html:1344 #: assets/templates/assets/detail_generic.html:1344
msgid "Endommagé" msgid "Endommagé"
msgstr "Damaged" msgstr "Damaged"
@ -925,21 +924,18 @@ msgstr "Removed"
msgid "Archivé" msgid "Archivé"
msgstr "Archived" msgstr "Archived"
#: assets/models/core.py:244 assets/models/inspection.py:276 #: assets/models/core.py:244
#: assets/models/parking.py:32 #: assets/models/parking.py:32
#: assets/templates/assets/components/inspection_form_modal.html:128
msgid "Bon état" msgid "Bon état"
msgstr "Good condition" msgstr "Good condition"
#: assets/models/core.py:245 assets/models/inspection.py:277 #: assets/models/core.py:245
#: assets/models/parking.py:33 #: assets/models/parking.py:33
#: assets/templates/assets/components/inspection_form_modal.html:129
msgid "Moyen" msgid "Moyen"
msgstr "Fair" msgstr "Fair"
#: assets/models/core.py:246 assets/models/inspection.py:278 #: assets/models/core.py:246
#: assets/models/parking.py:34 #: assets/models/parking.py:34
#: assets/templates/assets/components/inspection_form_modal.html:130
msgid "Dégradé" msgid "Dégradé"
msgstr "Degraded" msgstr "Degraded"
@ -972,14 +968,12 @@ msgstr "Rejected"
msgid "À inspecter" msgid "À inspecter"
msgstr "To inspect" msgstr "To inspect"
#: assets/models/core.py:260 assets/models/inspection.py:12 #: assets/models/core.py:260
#: assets/templates/assets/components/inspection_form_modal.html:48
#: assets/views/core.py:852 #: assets/views/core.py:852
msgid "Conforme" msgid "Conforme"
msgstr "Normal" msgstr "Normal"
#: assets/models/core.py:261 #: assets/models/core.py:261
#: assets/templates/assets/components/inspection_form_modal.html:55
#: assets/views/core.py:853 #: assets/views/core.py:853
msgid "Non conforme" msgid "Non conforme"
msgstr "Non-compliant" msgstr "Non-compliant"
@ -998,7 +992,6 @@ msgstr "Funding program / Project"
#: assets/models/history.py:186 #: assets/models/history.py:186
#: assets/templates/assets/buildings/publicbuilding_detail.html:302 #: assets/templates/assets/buildings/publicbuilding_detail.html:302
#: assets/templates/assets/inspections_list.html:187
#: assets/templates/assets/outdoor/outdoorlocation_detail.html:330 #: assets/templates/assets/outdoor/outdoorlocation_detail.html:330
msgid "Type d'asset" msgid "Type d'asset"
msgstr "Asset type" msgstr "Asset type"
@ -1184,7 +1177,6 @@ msgstr "Last survey"
#: assets/templates/assets/nature_index.html:82 #: assets/templates/assets/nature_index.html:82
#: assets/templates/assets/publiclighting_index.html:29 #: assets/templates/assets/publiclighting_index.html:29
#: assets/templates/assets/publiclighting_index.html:73 #: assets/templates/assets/publiclighting_index.html:73
#: assets/templates/assets/quick_inspection_map.html:175
#: assets/templates/assets/roads_index.html:29 #: assets/templates/assets/roads_index.html:29
#: assets/templates/assets/roads_index.html:85 #: assets/templates/assets/roads_index.html:85
#: assets/templates/assets/sign_index.html:29 #: assets/templates/assets/sign_index.html:29
@ -1267,190 +1259,14 @@ msgstr "Geometry"
msgid "GeoJSON" msgid "GeoJSON"
msgstr "GeoJSON" msgstr "GeoJSON"
#: assets/models/inspection.py:13
msgid "Non conforme / Anomalie"
msgstr "Non-compliant / Anomaly"
#: assets/models/inspection.py:14
msgid "Données corrigées"
msgstr "Corrected data"
#: assets/models/inspection.py:16
msgid "Absent / Retiré"
msgstr "Absent / Removed"
#: assets/models/inspection.py:20
msgid "Inspection visuelle"
msgstr "Visual inspection"
#: assets/models/inspection.py:21
msgid "Inspection technique"
msgstr "Technical inspection"
#: assets/models/inspection.py:22
msgid "Suivi / Qualité post-intervention"
msgstr "Post-intervention monitoring / quality"
#: assets/models/inspection.py:26
msgid "Observer / Agent de terrain"
msgstr "Observer / Field agent"
#: assets/models/inspection.py:27
msgid "Prestataire technique externe"
msgstr "External technical provider"
#: assets/models/inspection.py:28
msgid "Gestionnaire / Contrôleur interne"
msgstr "Internal manager / controller"
#: assets/models/inspection.py:39
msgid "Type d'asset / Cible"
msgstr "Asset / Target type"
#: assets/models/inspection.py:42
msgid "ID de la cible"
msgstr "Target ID"
#: assets/models/inspection.py:49
msgid "Nature de l'inspection"
msgstr "Nature of inspection"
#: assets/models/inspection.py:55
msgid "Rôle de l'inspecteur"
msgstr "Role of inspector"
#: assets/models/inspection.py:64
msgid "Inspecteur / Vérificateur"
msgstr "Inspector / Verifier"
#: assets/models/inspection.py:66
msgid "Date d'inspection"
msgstr "Inspection date"
#: assets/models/inspection.py:72
#: assets/templates/assets/components/asset_inspections.html:104 #: assets/templates/assets/components/asset_inspections.html:104
#: assets/templates/assets/components/inspection_form_modal.html:41
#: assets/templates/assets/inspections_list.html:195
#: assets/templates/assets/quick_inspection_map.html:355
msgid "Résultat du contrôle" msgid "Résultat du contrôle"
msgstr "Control result" msgstr "Control result"
#: assets/models/inspection.py:79 #: assets/models/parking.py:35
msgid "État d'obsolescence constaté"
msgstr "Observed state of obsolescence"
#: assets/models/inspection.py:82
msgid "Remarques / Constats"
msgstr "Remarks / Findings"
#: assets/models/inspection.py:85
msgid "Appliquer directement les modifications à l'asset"
msgstr "Apply modifications directly to asset"
#: assets/models/inspection.py:92
msgid "Inspection d'asset (Générique)"
msgstr "Asset inspection (Generic)"
#: assets/models/inspection.py:93
msgid "Inspections d'assets (Génériques)"
msgstr "Asset inspections (Generic)"
#: assets/models/inspection.py:196
#, python-format
msgid "%(count)d arceaux classiques"
msgstr "%(count)d classic hoops"
#: assets/models/inspection.py:198
#, python-format
msgid "%(count)d arceaux cargo"
msgstr "%(count)d cargo hoops"
#: assets/models/inspection.py:200
msgid "Drop-zone trottinette"
msgstr "Scooter drop-zone"
#: assets/models/inspection.py:200
msgid "Sans drop-zone"
msgstr "Without drop-zone"
#: assets/models/inspection.py:203
msgid "Marquage bon état"
msgstr "Marking good condition"
#: assets/models/inspection.py:204
msgid "Marquage moyen"
msgstr "Marking average condition"
#: assets/models/inspection.py:205
msgid "Marquage dégradé"
msgstr "Marking degraded"
#: assets/models/inspection.py:206
msgid "Marquage absent / effacé"
msgstr "Marking missing / erased"
#: assets/models/inspection.py:207
msgid "Marquage mauvais"
msgstr "Marking poor"
#: assets/models/inspection.py:213
#, python-format
msgid "Obsolescence : %s"
msgstr "Obsolescence : %s"
#: assets/models/inspection.py:257
msgid "Nombre d'arceaux classiques constaté"
msgstr "Observed number of standard bike stands"
#: assets/models/inspection.py:262
msgid "Nombre d'arceaux cargo constaté"
msgstr "Observed number of cargo bike stands"
#: assets/models/inspection.py:268
msgid "Type d'arceau constaté"
msgstr "Observed bike stand type"
#: assets/models/inspection.py:273
msgid "Drop zone trottinette constatée"
msgstr "Observed scooter drop-zone"
#: assets/models/inspection.py:279 assets/models/parking.py:35
#: assets/templates/assets/components/inspection_form_modal.html:131
msgid "Absent / Effacé" msgid "Absent / Effacé"
msgstr "Absent / Deleted" msgstr "Absent / Deleted"
#: assets/models/inspection.py:286
msgid "Qualité du marquage constatée"
msgstr "Observed marking quality"
#: assets/models/inspection.py:290
msgid "Mettre à jour la capacité / comptages de l'asset"
msgstr "Update asset capacity / counts"
#: assets/models/inspection.py:294
msgid "Inspection d'emplacement de stationnement"
msgstr "Parking spot inspection"
#: assets/models/inspection.py:295
msgid "Inspections d'emplacements de stationnement"
msgstr "Parking spot inspections"
#: assets/models/inspection.py:333
msgid "Inspection"
msgstr "Inspection"
#: assets/models/inspection.py:338
msgid "Fichier / Photo / Document"
msgstr "File / Photo / Document"
#: assets/models/inspection.py:343
msgid "Document / Photo d'inspection"
msgstr "Inspection document / photo"
#: assets/models/inspection.py:344
msgid "Documents / Photos d'inspection"
msgstr "Inspection documents / photos"
#: assets/models/nature.py:81 #: assets/models/nature.py:81
msgid "Tulipes" msgid "Tulipes"
msgstr "Tulips" msgstr "Tulips"
@ -2918,7 +2734,6 @@ msgstr "You do not have permission to edit assets for this theme"
#: assets/templates/assets/clean_index.html:8 #: assets/templates/assets/clean_index.html:8
#: assets/templates/assets/controlcenters_index.html:16 #: assets/templates/assets/controlcenters_index.html:16
#: assets/templates/assets/controlcenters_index.html:45 #: assets/templates/assets/controlcenters_index.html:45
#: assets/templates/assets/inspections_list.html:83
#: assets/templates/assets/its_index.html:16 #: assets/templates/assets/its_index.html:16
#: assets/templates/assets/its_index.html:56 #: assets/templates/assets/its_index.html:56
#: assets/templates/assets/nature_index.html:28 #: assets/templates/assets/nature_index.html:28
@ -2945,7 +2760,6 @@ msgstr "Filter"
#: assets/templates/assets/clean_index.html:9 #: assets/templates/assets/clean_index.html:9
#: assets/templates/assets/controlcenters_index.html:17 #: assets/templates/assets/controlcenters_index.html:17
#: assets/templates/assets/controlcenters_index.html:46 #: assets/templates/assets/controlcenters_index.html:46
#: assets/templates/assets/inspections_list.html:84
#: assets/templates/assets/its_index.html:17 #: assets/templates/assets/its_index.html:17
#: assets/templates/assets/its_index.html:57 #: assets/templates/assets/its_index.html:57
#: assets/templates/assets/nature_index.html:29 #: assets/templates/assets/nature_index.html:29
@ -3223,16 +3037,12 @@ msgstr "Asset totals"
#: assets/templates/assets/components/asset_interventions.html:112 #: assets/templates/assets/components/asset_interventions.html:112
#: assets/templates/assets/components/asset_location_link.html:50 #: assets/templates/assets/components/asset_location_link.html:50
#: assets/templates/assets/components/asset_model_card.html:62 #: assets/templates/assets/components/asset_model_card.html:62
#: assets/templates/assets/components/inspection_form_modal.html:11
#: assets/templates/assets/components/inspection_form_modal.html:172
#: assets/templates/assets/components/inspection_form_modal.html:204
#: assets/templates/assets/components/location_edit_modals.html:24 #: assets/templates/assets/components/location_edit_modals.html:24
#: assets/templates/assets/components/location_edit_modals.html:56 #: assets/templates/assets/components/location_edit_modals.html:56
#: assets/templates/assets/components/location_edit_modals.html:82 #: assets/templates/assets/components/location_edit_modals.html:82
#: assets/templates/assets/controlcenters_location_detail.html:112 #: assets/templates/assets/controlcenters_location_detail.html:112
#: assets/templates/assets/controlcenters_location_detail.html:145 #: assets/templates/assets/controlcenters_location_detail.html:145
#: assets/templates/assets/controlcenters_location_detail.html:170 #: assets/templates/assets/controlcenters_location_detail.html:170
#: assets/templates/assets/inspections_list.html:299
#: assets/templates/assets/its_location_detail.html:55 #: assets/templates/assets/its_location_detail.html:55
#: assets/templates/assets/its_location_detail.html:88 #: assets/templates/assets/its_location_detail.html:88
#: assets/templates/assets/naturerwiz_detail.html:273 #: assets/templates/assets/naturerwiz_detail.html:273
@ -3288,7 +3098,6 @@ msgstr "Choose export format:"
#: assets/templates/assets/components/asset_interventions.html:173 #: assets/templates/assets/components/asset_interventions.html:173
#: assets/templates/assets/components/asset_location_link.html:75 #: assets/templates/assets/components/asset_location_link.html:75
#: assets/templates/assets/components/asset_model_card.html:98 #: assets/templates/assets/components/asset_model_card.html:98
#: assets/templates/assets/components/inspection_form_modal.html:152
#: assets/templates/assets/components/location_edit_modals.html:38 #: assets/templates/assets/components/location_edit_modals.html:38
#: assets/templates/assets/components/location_edit_modals.html:63 #: assets/templates/assets/components/location_edit_modals.html:63
#: assets/templates/assets/components/location_edit_modals.html:96 #: assets/templates/assets/components/location_edit_modals.html:96
@ -4229,8 +4038,6 @@ msgstr "Perform an inspection"
#: assets/templates/assets/components/asset_inspections.html:33 #: assets/templates/assets/components/asset_inspections.html:33
#: assets/templates/assets/components/asset_inspections.html:100 #: assets/templates/assets/components/asset_inspections.html:100
#: assets/templates/assets/inspections_list.html:191
#: assets/templates/assets/quick_inspection_map.html:351
msgid "Date & Heure" msgid "Date & Heure"
msgstr "Date & Time" msgstr "Date & Time"
@ -4240,21 +4047,16 @@ msgstr "Control status"
#: assets/templates/assets/components/asset_inspections.html:35 #: assets/templates/assets/components/asset_inspections.html:35
#: assets/templates/assets/components/asset_inspections.html:108 #: assets/templates/assets/components/asset_inspections.html:108
#: assets/templates/assets/inspections_list.html:199
#: assets/templates/assets/quick_inspection_map.html:359
#: assets/views/locations.py:28 #: assets/views/locations.py:28
msgid "Nature" msgid "Nature"
msgstr "Nature" msgstr "Nature"
#: assets/templates/assets/components/asset_inspections.html:36 #: assets/templates/assets/components/asset_inspections.html:36
#: assets/templates/assets/components/asset_inspections.html:112 #: assets/templates/assets/components/asset_inspections.html:112
#: assets/templates/assets/inspections_list.html:203
#: assets/templates/assets/quick_inspection_map.html:363
msgid "Inspecteur" msgid "Inspecteur"
msgstr "Inspector" msgstr "Inspector"
#: assets/templates/assets/components/asset_inspections.html:37 #: assets/templates/assets/components/asset_inspections.html:37
#: assets/templates/assets/inspections_list.html:231
msgid "Remarques" msgid "Remarques"
msgstr "Remarks" msgstr "Remarks"
@ -4271,12 +4073,10 @@ msgid "Équipement cible"
msgstr "Target equipment" msgstr "Target equipment"
#: assets/templates/assets/components/asset_inspections.html:119 #: assets/templates/assets/components/asset_inspections.html:119
#: assets/templates/assets/inspections_list.html:210
msgid "Arceaux classiques constatés" msgid "Arceaux classiques constatés"
msgstr "Observed standard bike racks" msgstr "Observed standard bike racks"
#: assets/templates/assets/components/asset_inspections.html:125 #: assets/templates/assets/components/asset_inspections.html:125
#: assets/templates/assets/inspections_list.html:216
msgid "Arceaux cargo constatés" msgid "Arceaux cargo constatés"
msgstr "Observed cargo bike racks" msgstr "Observed cargo bike racks"
@ -4285,7 +4085,6 @@ msgid "Remarques / Observations"
msgstr "Remarks / Observations" msgstr "Remarks / Observations"
#: assets/templates/assets/components/asset_inspections.html:143 #: assets/templates/assets/components/asset_inspections.html:143
#: assets/templates/assets/inspections_list.html:246
msgid "Photos & Documents joints" msgid "Photos & Documents joints"
msgstr "Photos & Attached documents" msgstr "Photos & Attached documents"
@ -4466,8 +4265,6 @@ msgid "Modèle actuel :"
msgstr "Current model:" msgstr "Current model:"
#: assets/templates/assets/components/asset_model_card.html:77 #: assets/templates/assets/components/asset_model_card.html:77
#: assets/templates/assets/quick_inspection_map.html:266
#: assets/templates/assets/quick_inspection_map.html:267
msgid "Toutes les catégories" msgid "Toutes les catégories"
msgstr "All categories" msgstr "All categories"
@ -4479,92 +4276,10 @@ msgstr "New model"
msgid "Sélectionner un modèle..." msgid "Sélectionner un modèle..."
msgstr "Select a model..." msgstr "Select a model..."
#: assets/templates/assets/components/inspection_form_modal.html:9
msgid "Nouvelle inspection"
msgstr "New inspection"
#: assets/templates/assets/components/inspection_form_modal.html:31
msgid "Statut actuel"
msgstr "Current status"
#: assets/templates/assets/components/inspection_form_modal.html:42
msgid "Résultat"
msgstr "Result"
#: assets/templates/assets/components/inspection_form_modal.html:71
msgid "Absent"
msgstr "Missing"
#: assets/templates/assets/components/inspection_form_modal.html:78
msgid "Corrigé"
msgstr "Corrected"
#: assets/templates/assets/components/inspection_form_modal.html:88
msgid "Photos & Fichiers"
msgstr "Photos & Files"
#: assets/templates/assets/components/inspection_form_modal.html:94
msgid "Prendre des photos (mode continu)"
msgstr "Take photos (continuous mode)"
#: assets/templates/assets/components/inspection_form_modal.html:98
msgid "Photos, vidéos, fichiers"
msgstr "Photos, videos, files"
#: assets/templates/assets/components/inspection_form_modal.html:113
msgid "Contrôles spécifiques stationnement"
msgstr "Specific parking controls"
#: assets/templates/assets/components/inspection_form_modal.html:116
msgid "Arceaux classiques"
msgstr "Standard bike racks"
#: assets/templates/assets/components/inspection_form_modal.html:120
msgid "Arceaux cargo"
msgstr "Cargo bike racks"
#: assets/templates/assets/components/inspection_form_modal.html:125
msgid "Qualité marquage"
msgstr "Marking quality"
#: assets/templates/assets/components/inspection_form_modal.html:127
msgid "-- Sélectionner --"
msgstr "-- Select --"
#: assets/templates/assets/components/inspection_form_modal.html:140
msgid "Créer automatiquement une intervention en cas d'anomalie / dégât"
msgstr "Automatically create an intervention upon anomaly / damage"
#: assets/templates/assets/components/inspection_form_modal.html:146
msgid "Remarques / Remarques du constat"
msgstr "Remarks / Finding notes"
#: assets/templates/assets/components/inspection_form_modal.html:147
msgid "Saisir vos remarques ou détails..."
msgstr "Enter your remarks or details..."
#: assets/templates/assets/components/inspection_form_modal.html:156
msgid "Valider l'inspection"
msgstr "Validate inspection"
#: assets/templates/assets/components/inspection_form_modal.html:170
msgid "Caméra (Mode continu)"
msgstr "Camera (Continuous mode)"
#: assets/templates/assets/components/inspection_form_modal.html:185
#: assets/templates/assets/components/map_polygon_picker.html:37 #: assets/templates/assets/components/map_polygon_picker.html:37
msgid "Terminer" msgid "Terminer"
msgstr "Finish" msgstr "Finish"
#: assets/templates/assets/components/inspection_form_modal.html:188
msgid "Capturer"
msgstr "Capture"
#: assets/templates/assets/components/inspection_form_modal.html:202
msgid "Choisir un équipement à inspecter"
msgstr "Select equipment to inspect"
#: assets/templates/assets/components/inspection_form_modal.html:207
msgid "" msgid ""
"Plusieurs équipements ont été détectés à cet emplacement. Veuillez " "Plusieurs équipements ont été détectés à cet emplacement. Veuillez "
"sélectionner celui à inspecter :" "sélectionner celui à inspecter :"
@ -4634,8 +4349,8 @@ msgstr ""
msgid "Supprimer" msgid "Supprimer"
msgstr "Delete" msgstr "Delete"
# , python-format
#: assets/templates/assets/components/location_edit_modals.html:80 #: assets/templates/assets/components/location_edit_modals.html:80
# , python-format
#, python-format #, python-format
msgid "Créer une nouvelle localisation (%(label)s)" msgid "Créer une nouvelle localisation (%(label)s)"
msgstr "Create a new location (%(label)s)" msgstr "Create a new location (%(label)s)"
@ -5141,98 +4856,6 @@ msgstr "Draft"
msgid "Aucune version trouvée." msgid "Aucune version trouvée."
msgstr "No version found." msgstr "No version found."
#: assets/templates/assets/inspections_list.html:4
#: assets/templates/assets/inspections_list.html:15
msgid "Inspections d'assets"
msgstr "Asset inspections"
#: assets/templates/assets/inspections_list.html:17
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Monitoring and history of field controls and verifications"
#: assets/templates/assets/inspections_list.html:22
msgid "Nouvelle inspection (Carte GPS)"
msgstr "New inspection (GPS map)"
#: assets/templates/assets/inspections_list.html:32
msgid "Mes inspections"
msgstr "My inspections"
#: assets/templates/assets/inspections_list.html:37
msgid "Toutes les inspections"
msgstr "All inspections"
#: assets/templates/assets/inspections_list.html:51
msgid "Rechercher par note, asset, ..."
msgstr "Search by note, asset, ..."
#: assets/templates/assets/inspections_list.html:57
msgid "-- Thématiques --"
msgstr "-- Topics --"
#: assets/templates/assets/inspections_list.html:66
msgid "-- Natures --"
msgstr "-- Natures --"
#: assets/templates/assets/inspections_list.html:75
msgid "-- Tous les résultats --"
msgstr "-- All results --"
#: assets/templates/assets/inspections_list.html:93
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspection found"
msgstr[1] "%(counter)s inspections found"
#: assets/templates/assets/inspections_list.html:94
msgid "limité aux 100 plus récentes"
msgstr "limited to 100 most recent"
#: assets/templates/assets/inspections_list.html:104
#: assets/templates/assets/inspections_list.html:105
msgid "Carte des résultats"
msgstr "Results map"
#: assets/templates/assets/inspections_list.html:148
msgid "Photos / Documents joints"
msgstr "Photos / Attached documents"
#: assets/templates/assets/inspections_list.html:167
#: assets/templates/assets/quick_inspection_map.html:333
msgid "Détail de l'inspection"
msgstr "Inspection details"
#: assets/templates/assets/inspections_list.html:176
#: assets/templates/assets/quick_inspection_map.html:341
msgid "Consulter la fiche de l'asset"
msgstr "View asset sheet"
#: assets/templates/assets/inspections_list.html:183
#: assets/templates/assets/quick_inspection_map.html:347
msgid "Cible"
msgstr "Target"
#: assets/templates/assets/inspections_list.html:222
msgid "Qualité du marquage"
msgstr "Marking quality"
#: assets/templates/assets/inspections_list.html:238 assets/views/export.py:100
msgid "Obsolescence"
msgstr "Obsolescence"
#: assets/templates/assets/inspections_list.html:309
msgid "Aucune inspection trouvée"
msgstr "No inspection found"
#: assets/templates/assets/inspections_list.html:310
msgid "Aucune inspection ne correspond à vos critères de recherche."
msgstr "No inspection matches your search criteria."
#: assets/templates/assets/inspections_list.html:419
msgid "Carte des résultats d'inspections"
msgstr "Inspection results map"
#: assets/templates/assets/its_index.html:27 #: assets/templates/assets/its_index.html:27
msgid "Créer une nouvelle localisation ITS" msgid "Créer une nouvelle localisation ITS"
msgstr "Create new ITS location" msgstr "Create new ITS location"
@ -5788,99 +5411,17 @@ msgstr "Poles"
msgid "Alimentation:" msgid "Alimentation:"
msgstr "Power supply:" msgstr "Power supply:"
#: assets/templates/assets/quick_inspection_map.html:4
msgid "Inspection Rapide Carte GPS"
msgstr "Quick Inspection GPS Map"
#: assets/templates/assets/quick_inspection_map.html:160
msgid "Inspection Rapide"
msgstr "Quick Inspection"
#: assets/templates/assets/quick_inspection_map.html:161
msgid "GPS actif"
msgstr "GPS active"
#: assets/templates/assets/quick_inspection_map.html:168
#: assets/templates/assets/unified_index.html:91 #: assets/templates/assets/unified_index.html:91
#: assets/templates/assets/unified_index.html:251 #: assets/templates/assets/unified_index.html:251
#: assets/templates/assets/unified_index.html:261 #: assets/templates/assets/unified_index.html:261
msgid "Thématique" msgid "Thématique"
msgstr "Topic" msgstr "Topic"
#: assets/templates/assets/quick_inspection_map.html:182
msgid "Inspections (3 mois)"
msgstr "Inspections (3 months)"
#: assets/templates/assets/quick_inspection_map.html:191
msgid "-- Thématique --"
msgstr "-- Topic --"
#: assets/templates/assets/quick_inspection_map.html:198
msgid "-- Catégorie --"
msgstr "-- Category --"
#: assets/templates/assets/quick_inspection_map.html:206
msgid "Veuillez zoomer sur la carte pour afficher les équipements."
msgstr "Please zoom in on the map to display equipment."
#: assets/templates/assets/quick_inspection_map.html:216
msgid "Ma position GPS"
msgstr "My GPS position"
#: assets/templates/assets/quick_inspection_map.html:228
msgid "Choisir une thématique"
msgstr "Select a topic"
#: assets/templates/assets/quick_inspection_map.html:260
msgid "Filtrer par catégorie"
msgstr "Filter by category"
#: assets/templates/assets/quick_inspection_map.html:288
msgid "Historique des inspections sur la carte"
msgstr "Inspection history on the map"
#: assets/templates/assets/quick_inspection_map.html:294
msgid "" msgid ""
"Choisissez la durée d'affichage des contrôles et inspections effectués dans " "Choisissez la durée d'affichage des contrôles et inspections effectués dans "
"la zone." "la zone."
msgstr "" msgstr ""
#: assets/templates/assets/quick_inspection_map.html:298
msgid "Désactiver l'affichage"
msgstr "Disable display"
#: assets/templates/assets/quick_inspection_map.html:302
msgid "1 mois"
msgstr "1 month"
#: assets/templates/assets/quick_inspection_map.html:306
msgid "3 mois (par défaut)"
msgstr "3 months (default)"
#: assets/templates/assets/quick_inspection_map.html:310
msgid "6 mois"
msgstr "6 months"
#: assets/templates/assets/quick_inspection_map.html:314
msgid "1 an"
msgstr "1 year"
#: assets/templates/assets/quick_inspection_map.html:318
msgid "Tout (historique complet)"
msgstr "All (complete history)"
#: assets/templates/assets/quick_inspection_map.html:370
msgid "Constats & Remarques"
msgstr "Findings & Remarks"
#: assets/templates/assets/quick_inspection_map.html:378
msgid "Galerie photos"
msgstr "Photo gallery"
#: assets/templates/assets/quick_inspection_map.html:397
msgid "Documents joints"
msgstr "Attached documents"
#: assets/templates/assets/roads_index.html:49 #: assets/templates/assets/roads_index.html:49
msgid "Qualité globale" msgid "Qualité globale"
msgstr "Overall quality" msgstr "Overall quality"
@ -7563,34 +7104,10 @@ msgstr "Invalid date format. Use ISO 8601."
msgid "Inexistant" msgid "Inexistant"
msgstr "Nonexistent" msgstr "Nonexistent"
#: assets/views/inspections.py:184
msgid "" msgid ""
"Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections." "Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections."
msgstr "" msgstr ""
#: assets/views/inspections.py:251
msgid "Cible d'inspection non spécifiée."
msgstr "Inspection target not specified."
#: assets/views/inspections.py:257
#, python-format
msgid "Cible introuvable : %s"
msgstr "Cible introuvable : %s"
#: assets/views/inspections.py:328
#, python-format
msgid "Intervention suite à inspection non conforme (%s)"
msgstr "Intervention following non-compliant inspection (%s)"
#: assets/views/inspections.py:332
#, python-format
msgid "Signalement automatique lors de l'inspection du %s."
msgstr "Automatic reporting when inspecting %s."
#: assets/views/inspections.py:348
msgid "Inspection enregistrée avec succès."
msgstr "Inspection saved successfully."
#: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463 #: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463
#: assets/views/nature.py:543 assets/views/publiclighting.py:457 #: assets/views/nature.py:543 assets/views/publiclighting.py:457
#: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499 #: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499
@ -7732,3 +7249,4 @@ msgstr "GIEP zones"
#: assets/views/water.py:572 #: assets/views/water.py:572
msgid "Aucune localisation GIEP trouvée à proximité" msgid "Aucune localisation GIEP trouvée à proximité"
msgstr "No location found" msgstr "No location found"

View file

@ -903,8 +903,7 @@ msgstr "Planifié"
msgid "Actif" msgid "Actif"
msgstr "Actif" msgstr "Actif"
#: assets/models/core.py:236 assets/models/inspection.py:15 #: assets/models/core.py:236
#: assets/templates/assets/components/inspection_form_modal.html:64
#: assets/templates/assets/detail_generic.html:1344 #: assets/templates/assets/detail_generic.html:1344
msgid "Endommagé" msgid "Endommagé"
msgstr "Endommagé" msgstr "Endommagé"
@ -926,21 +925,18 @@ msgstr "Enlevé"
msgid "Archivé" msgid "Archivé"
msgstr "Archivé" msgstr "Archivé"
#: assets/models/core.py:244 assets/models/inspection.py:276 #: assets/models/core.py:244
#: assets/models/parking.py:32 #: assets/models/parking.py:32
#: assets/templates/assets/components/inspection_form_modal.html:128
msgid "Bon état" msgid "Bon état"
msgstr "Bon état" msgstr "Bon état"
#: assets/models/core.py:245 assets/models/inspection.py:277 #: assets/models/core.py:245
#: assets/models/parking.py:33 #: assets/models/parking.py:33
#: assets/templates/assets/components/inspection_form_modal.html:129
msgid "Moyen" msgid "Moyen"
msgstr "Moyen" msgstr "Moyen"
#: assets/models/core.py:246 assets/models/inspection.py:278 #: assets/models/core.py:246
#: assets/models/parking.py:34 #: assets/models/parking.py:34
#: assets/templates/assets/components/inspection_form_modal.html:130
msgid "Dégradé" msgid "Dégradé"
msgstr "Dégradé" msgstr "Dégradé"
@ -973,14 +969,12 @@ msgstr "Rejeté"
msgid "À inspecter" msgid "À inspecter"
msgstr "À inspecter" msgstr "À inspecter"
#: assets/models/core.py:260 assets/models/inspection.py:12 #: assets/models/core.py:260
#: assets/templates/assets/components/inspection_form_modal.html:48
#: assets/views/core.py:852 #: assets/views/core.py:852
msgid "Conforme" msgid "Conforme"
msgstr "Normal" msgstr "Normal"
#: assets/models/core.py:261 #: assets/models/core.py:261
#: assets/templates/assets/components/inspection_form_modal.html:55
#: assets/views/core.py:853 #: assets/views/core.py:853
msgid "Non conforme" msgid "Non conforme"
msgstr "Non conforme" msgstr "Non conforme"
@ -999,7 +993,6 @@ msgstr "Programme de financement / Projet"
#: assets/models/history.py:186 #: assets/models/history.py:186
#: assets/templates/assets/buildings/publicbuilding_detail.html:302 #: assets/templates/assets/buildings/publicbuilding_detail.html:302
#: assets/templates/assets/inspections_list.html:187
#: assets/templates/assets/outdoor/outdoorlocation_detail.html:330 #: assets/templates/assets/outdoor/outdoorlocation_detail.html:330
msgid "Type d'asset" msgid "Type d'asset"
msgstr "Type d'asset" msgstr "Type d'asset"
@ -1185,7 +1178,6 @@ msgstr "Dernier relevé"
#: assets/templates/assets/nature_index.html:82 #: assets/templates/assets/nature_index.html:82
#: assets/templates/assets/publiclighting_index.html:29 #: assets/templates/assets/publiclighting_index.html:29
#: assets/templates/assets/publiclighting_index.html:73 #: assets/templates/assets/publiclighting_index.html:73
#: assets/templates/assets/quick_inspection_map.html:175
#: assets/templates/assets/roads_index.html:29 #: assets/templates/assets/roads_index.html:29
#: assets/templates/assets/roads_index.html:85 #: assets/templates/assets/roads_index.html:85
#: assets/templates/assets/sign_index.html:29 #: assets/templates/assets/sign_index.html:29
@ -1268,190 +1260,14 @@ msgstr "Géométrie"
msgid "GeoJSON" msgid "GeoJSON"
msgstr "GeoJSON" msgstr "GeoJSON"
#: assets/models/inspection.py:13
msgid "Non conforme / Anomalie"
msgstr "Non conforme / Anomalie"
#: assets/models/inspection.py:14
msgid "Données corrigées"
msgstr "Données corrigées"
#: assets/models/inspection.py:16
msgid "Absent / Retiré"
msgstr "Absent / Retiré"
#: assets/models/inspection.py:20
msgid "Inspection visuelle"
msgstr "Intersection introuvable"
#: assets/models/inspection.py:21
msgid "Inspection technique"
msgstr "Inspection technique"
#: assets/models/inspection.py:22
msgid "Suivi / Qualité post-intervention"
msgstr "Suivi / Qualité post-intervention"
#: assets/models/inspection.py:26
msgid "Observer / Agent de terrain"
msgstr "Observer / Agent de terrain"
#: assets/models/inspection.py:27
msgid "Prestataire technique externe"
msgstr "Prestataire maintenance contrôleurs"
#: assets/models/inspection.py:28
msgid "Gestionnaire / Contrôleur interne"
msgstr "Gestionnaire / Contrôleur interne"
#: assets/models/inspection.py:39
msgid "Type d'asset / Cible"
msgstr "Type d'asset à lier"
#: assets/models/inspection.py:42
msgid "ID de la cible"
msgstr "ID de l'asset"
#: assets/models/inspection.py:49
msgid "Nature de l'inspection"
msgstr "Date d'installation"
#: assets/models/inspection.py:55
msgid "Rôle de l'inspecteur"
msgstr "Rôle de l'inspecteur"
#: assets/models/inspection.py:64
msgid "Inspecteur / Vérificateur"
msgstr "Inspecteur / Vérificateur"
#: assets/models/inspection.py:66
msgid "Date d'inspection"
msgstr "Date d'installation"
#: assets/models/inspection.py:72
#: assets/templates/assets/components/asset_inspections.html:104 #: assets/templates/assets/components/asset_inspections.html:104
#: assets/templates/assets/components/inspection_form_modal.html:41
#: assets/templates/assets/inspections_list.html:195
#: assets/templates/assets/quick_inspection_map.html:355
msgid "Résultat du contrôle" msgid "Résultat du contrôle"
msgstr "Centre de contrôle" msgstr "Résultat du contrôle"
#: assets/models/inspection.py:79 #: assets/models/parking.py:35
msgid "État d'obsolescence constaté"
msgstr "État d'obsolescence constaté"
#: assets/models/inspection.py:82
msgid "Remarques / Constats"
msgstr "Remarque sol"
#: assets/models/inspection.py:85
msgid "Appliquer directement les modifications à l'asset"
msgstr "Appliquer directement les modifications à l'asset"
#: assets/models/inspection.py:92
msgid "Inspection d'asset (Générique)"
msgstr "Inspection d'asset (Générique)"
#: assets/models/inspection.py:93
msgid "Inspections d'assets (Génériques)"
msgstr "Inspections d'assets (Génériques)"
#: assets/models/inspection.py:196
#, python-format
msgid "%(count)d arceaux classiques"
msgstr "%(count)d arceaux classiques"
#: assets/models/inspection.py:198
#, python-format
msgid "%(count)d arceaux cargo"
msgstr "%(count)d arceaux cargo"
#: assets/models/inspection.py:200
msgid "Drop-zone trottinette"
msgstr "Drop-zone trottinette"
#: assets/models/inspection.py:200
msgid "Sans drop-zone"
msgstr "Sans drop-zone"
#: assets/models/inspection.py:203
msgid "Marquage bon état"
msgstr "Marquage bon état"
#: assets/models/inspection.py:204
msgid "Marquage moyen"
msgstr "Marque"
#: assets/models/inspection.py:205
msgid "Marquage dégradé"
msgstr "Marquage dégradé"
#: assets/models/inspection.py:206
msgid "Marquage absent / effacé"
msgstr "Marquage absent / effacé"
#: assets/models/inspection.py:207
msgid "Marquage mauvais"
msgstr "Marquage mauvais"
#: assets/models/inspection.py:213
#, python-format
msgid "Obsolescence : %s"
msgstr "Obsolescence : %s"
#: assets/models/inspection.py:257
msgid "Nombre d'arceaux classiques constaté"
msgstr "Nombre d'arceaux classiques constaté"
#: assets/models/inspection.py:262
msgid "Nombre d'arceaux cargo constaté"
msgstr "Nombre d'arceaux cargo constaté"
#: assets/models/inspection.py:268
msgid "Type d'arceau constaté"
msgstr "Type de clé"
#: assets/models/inspection.py:273
msgid "Drop zone trottinette constatée"
msgstr "Drop zone trottinette constatée"
#: assets/models/inspection.py:279 assets/models/parking.py:35
#: assets/templates/assets/components/inspection_form_modal.html:131
msgid "Absent / Effacé" msgid "Absent / Effacé"
msgstr "Absent / Effacé" msgstr "Absent / Effacé"
#: assets/models/inspection.py:286
msgid "Qualité du marquage constatée"
msgstr "Qualité du marquage constatée"
#: assets/models/inspection.py:290
msgid "Mettre à jour la capacité / comptages de l'asset"
msgstr "Créer l'asset"
#: assets/models/inspection.py:294
msgid "Inspection d'emplacement de stationnement"
msgstr "Inspection d'emplacement de stationnement"
#: assets/models/inspection.py:295
msgid "Inspections d'emplacements de stationnement"
msgstr "Inspections d'emplacements de stationnement"
#: assets/models/inspection.py:333
msgid "Inspection"
msgstr "Intersections"
#: assets/models/inspection.py:338
msgid "Fichier / Photo / Document"
msgstr "Fichier / Photo / Document"
#: assets/models/inspection.py:343
msgid "Document / Photo d'inspection"
msgstr "Document / Photo d'inspection"
#: assets/models/inspection.py:344
msgid "Documents / Photos d'inspection"
msgstr "Documents / Photos d'inspection"
#: assets/models/nature.py:81 #: assets/models/nature.py:81
msgid "Tulipes" msgid "Tulipes"
msgstr "Tulipes" msgstr "Tulipes"
@ -2919,7 +2735,6 @@ msgstr "You do not have permission to edit assets for this thematic"
#: assets/templates/assets/clean_index.html:8 #: assets/templates/assets/clean_index.html:8
#: assets/templates/assets/controlcenters_index.html:16 #: assets/templates/assets/controlcenters_index.html:16
#: assets/templates/assets/controlcenters_index.html:45 #: assets/templates/assets/controlcenters_index.html:45
#: assets/templates/assets/inspections_list.html:83
#: assets/templates/assets/its_index.html:16 #: assets/templates/assets/its_index.html:16
#: assets/templates/assets/its_index.html:56 #: assets/templates/assets/its_index.html:56
#: assets/templates/assets/nature_index.html:28 #: assets/templates/assets/nature_index.html:28
@ -2946,7 +2761,6 @@ msgstr "Filtrer"
#: assets/templates/assets/clean_index.html:9 #: assets/templates/assets/clean_index.html:9
#: assets/templates/assets/controlcenters_index.html:17 #: assets/templates/assets/controlcenters_index.html:17
#: assets/templates/assets/controlcenters_index.html:46 #: assets/templates/assets/controlcenters_index.html:46
#: assets/templates/assets/inspections_list.html:84
#: assets/templates/assets/its_index.html:17 #: assets/templates/assets/its_index.html:17
#: assets/templates/assets/its_index.html:57 #: assets/templates/assets/its_index.html:57
#: assets/templates/assets/nature_index.html:29 #: assets/templates/assets/nature_index.html:29
@ -3224,16 +3038,12 @@ msgstr "Totaux des assets"
#: assets/templates/assets/components/asset_interventions.html:112 #: assets/templates/assets/components/asset_interventions.html:112
#: assets/templates/assets/components/asset_location_link.html:50 #: assets/templates/assets/components/asset_location_link.html:50
#: assets/templates/assets/components/asset_model_card.html:62 #: assets/templates/assets/components/asset_model_card.html:62
#: assets/templates/assets/components/inspection_form_modal.html:11
#: assets/templates/assets/components/inspection_form_modal.html:172
#: assets/templates/assets/components/inspection_form_modal.html:204
#: assets/templates/assets/components/location_edit_modals.html:24 #: assets/templates/assets/components/location_edit_modals.html:24
#: assets/templates/assets/components/location_edit_modals.html:56 #: assets/templates/assets/components/location_edit_modals.html:56
#: assets/templates/assets/components/location_edit_modals.html:82 #: assets/templates/assets/components/location_edit_modals.html:82
#: assets/templates/assets/controlcenters_location_detail.html:112 #: assets/templates/assets/controlcenters_location_detail.html:112
#: assets/templates/assets/controlcenters_location_detail.html:145 #: assets/templates/assets/controlcenters_location_detail.html:145
#: assets/templates/assets/controlcenters_location_detail.html:170 #: assets/templates/assets/controlcenters_location_detail.html:170
#: assets/templates/assets/inspections_list.html:299
#: assets/templates/assets/its_location_detail.html:55 #: assets/templates/assets/its_location_detail.html:55
#: assets/templates/assets/its_location_detail.html:88 #: assets/templates/assets/its_location_detail.html:88
#: assets/templates/assets/naturerwiz_detail.html:273 #: assets/templates/assets/naturerwiz_detail.html:273
@ -3289,7 +3099,6 @@ msgstr "Choisissez le format d'export :"
#: assets/templates/assets/components/asset_interventions.html:173 #: assets/templates/assets/components/asset_interventions.html:173
#: assets/templates/assets/components/asset_location_link.html:75 #: assets/templates/assets/components/asset_location_link.html:75
#: assets/templates/assets/components/asset_model_card.html:98 #: assets/templates/assets/components/asset_model_card.html:98
#: assets/templates/assets/components/inspection_form_modal.html:152
#: assets/templates/assets/components/location_edit_modals.html:38 #: assets/templates/assets/components/location_edit_modals.html:38
#: assets/templates/assets/components/location_edit_modals.html:63 #: assets/templates/assets/components/location_edit_modals.html:63
#: assets/templates/assets/components/location_edit_modals.html:96 #: assets/templates/assets/components/location_edit_modals.html:96
@ -4228,8 +4037,6 @@ msgstr "Dernière inspection"
#: assets/templates/assets/components/asset_inspections.html:33 #: assets/templates/assets/components/asset_inspections.html:33
#: assets/templates/assets/components/asset_inspections.html:100 #: assets/templates/assets/components/asset_inspections.html:100
#: assets/templates/assets/inspections_list.html:191
#: assets/templates/assets/quick_inspection_map.html:351
msgid "Date & Heure" msgid "Date & Heure"
msgstr "Date / heure" msgstr "Date / heure"
@ -4239,21 +4046,16 @@ msgstr "Centre de contrôle"
#: assets/templates/assets/components/asset_inspections.html:35 #: assets/templates/assets/components/asset_inspections.html:35
#: assets/templates/assets/components/asset_inspections.html:108 #: assets/templates/assets/components/asset_inspections.html:108
#: assets/templates/assets/inspections_list.html:199
#: assets/templates/assets/quick_inspection_map.html:359
#: assets/views/locations.py:28 #: assets/views/locations.py:28
msgid "Nature" msgid "Nature"
msgstr "Nature" msgstr "Nature"
#: assets/templates/assets/components/asset_inspections.html:36 #: assets/templates/assets/components/asset_inspections.html:36
#: assets/templates/assets/components/asset_inspections.html:112 #: assets/templates/assets/components/asset_inspections.html:112
#: assets/templates/assets/inspections_list.html:203
#: assets/templates/assets/quick_inspection_map.html:363
msgid "Inspecteur" msgid "Inspecteur"
msgstr "Détecteur" msgstr "Détecteur"
#: assets/templates/assets/components/asset_inspections.html:37 #: assets/templates/assets/components/asset_inspections.html:37
#: assets/templates/assets/inspections_list.html:231
msgid "Remarques" msgid "Remarques"
msgstr "Remarque" msgstr "Remarque"
@ -4270,12 +4072,10 @@ msgid "Équipement cible"
msgstr "Type d'élément" msgstr "Type d'élément"
#: assets/templates/assets/components/asset_inspections.html:119 #: assets/templates/assets/components/asset_inspections.html:119
#: assets/templates/assets/inspections_list.html:210
msgid "Arceaux classiques constatés" msgid "Arceaux classiques constatés"
msgstr "Arceaux classiques constatés" msgstr "Arceaux classiques constatés"
#: assets/templates/assets/components/asset_inspections.html:125 #: assets/templates/assets/components/asset_inspections.html:125
#: assets/templates/assets/inspections_list.html:216
msgid "Arceaux cargo constatés" msgid "Arceaux cargo constatés"
msgstr "Arceaux cargo constatés" msgstr "Arceaux cargo constatés"
@ -4284,7 +4084,6 @@ msgid "Remarques / Observations"
msgstr "Observations" msgstr "Observations"
#: assets/templates/assets/components/asset_inspections.html:143 #: assets/templates/assets/components/asset_inspections.html:143
#: assets/templates/assets/inspections_list.html:246
msgid "Photos & Documents joints" msgid "Photos & Documents joints"
msgstr "Photos & Documents joints" msgstr "Photos & Documents joints"
@ -4465,8 +4264,6 @@ msgid "Modèle actuel :"
msgstr "Modèle actuel :" msgstr "Modèle actuel :"
#: assets/templates/assets/components/asset_model_card.html:77 #: assets/templates/assets/components/asset_model_card.html:77
#: assets/templates/assets/quick_inspection_map.html:266
#: assets/templates/assets/quick_inspection_map.html:267
msgid "Toutes les catégories" msgid "Toutes les catégories"
msgstr "Toutes les catégories" msgstr "Toutes les catégories"
@ -4478,92 +4275,10 @@ msgstr "Nouveau modèle"
msgid "Sélectionner un modèle..." msgid "Sélectionner un modèle..."
msgstr "Sélectionner un modèle..." msgstr "Sélectionner un modèle..."
#: assets/templates/assets/components/inspection_form_modal.html:9
msgid "Nouvelle inspection"
msgstr "No interventions"
#: assets/templates/assets/components/inspection_form_modal.html:31
msgid "Statut actuel"
msgstr "Statut"
#: assets/templates/assets/components/inspection_form_modal.html:42
msgid "Résultat"
msgstr "Résultat"
#: assets/templates/assets/components/inspection_form_modal.html:71
msgid "Absent"
msgstr "Absent"
#: assets/templates/assets/components/inspection_form_modal.html:78
msgid "Corrigé"
msgstr "Corrigé"
#: assets/templates/assets/components/inspection_form_modal.html:88
msgid "Photos & Fichiers"
msgstr "Choisir des fichiers"
#: assets/templates/assets/components/inspection_form_modal.html:94
msgid "Prendre des photos (mode continu)"
msgstr "Prendre des photos (mode continu)"
#: assets/templates/assets/components/inspection_form_modal.html:98
msgid "Photos, vidéos, fichiers"
msgstr "Choisir des fichiers"
#: assets/templates/assets/components/inspection_form_modal.html:113
msgid "Contrôles spécifiques stationnement"
msgstr "Contrôles spécifiques stationnement"
#: assets/templates/assets/components/inspection_form_modal.html:116
msgid "Arceaux classiques"
msgstr "Arceaux classiques"
#: assets/templates/assets/components/inspection_form_modal.html:120
msgid "Arceaux cargo"
msgstr "Arceaux cargo"
#: assets/templates/assets/components/inspection_form_modal.html:125
msgid "Qualité marquage"
msgstr "Qualité globale"
#: assets/templates/assets/components/inspection_form_modal.html:127
msgid "-- Sélectionner --"
msgstr "Sélectionner"
#: assets/templates/assets/components/inspection_form_modal.html:140
msgid "Créer automatiquement une intervention en cas d'anomalie / dégât"
msgstr "Créer automatiquement une intervention en cas d'anomalie / dégât"
#: assets/templates/assets/components/inspection_form_modal.html:146
msgid "Remarques / Remarques du constat"
msgstr "Remarques / Remarques du constat"
#: assets/templates/assets/components/inspection_form_modal.html:147
msgid "Saisir vos remarques ou détails..."
msgstr "Saisir vos remarques ou détails..."
#: assets/templates/assets/components/inspection_form_modal.html:156
msgid "Valider l'inspection"
msgstr "Valider la sélection"
#: assets/templates/assets/components/inspection_form_modal.html:170
msgid "Caméra (Mode continu)"
msgstr "Numéro de contrat"
#: assets/templates/assets/components/inspection_form_modal.html:185
#: assets/templates/assets/components/map_polygon_picker.html:37 #: assets/templates/assets/components/map_polygon_picker.html:37
msgid "Terminer" msgid "Terminer"
msgstr "Terminer" msgstr "Terminer"
#: assets/templates/assets/components/inspection_form_modal.html:188
msgid "Capturer"
msgstr "Nature"
#: assets/templates/assets/components/inspection_form_modal.html:202
msgid "Choisir un équipement à inspecter"
msgstr "Aucun équipement trouvé"
#: assets/templates/assets/components/inspection_form_modal.html:207
msgid "" msgid ""
"Plusieurs équipements ont été détectés à cet emplacement. Veuillez " "Plusieurs équipements ont été détectés à cet emplacement. Veuillez "
"sélectionner celui à inspecter :" "sélectionner celui à inspecter :"
@ -4635,8 +4350,8 @@ msgstr ""
msgid "Supprimer" msgid "Supprimer"
msgstr "Supprimer" msgstr "Supprimer"
# , python-format
#: assets/templates/assets/components/location_edit_modals.html:80 #: assets/templates/assets/components/location_edit_modals.html:80
# , python-format
#, python-format #, python-format
msgid "Créer une nouvelle localisation (%(label)s)" msgid "Créer une nouvelle localisation (%(label)s)"
msgstr "Créer une nouvelle localisation (%(label)s)" msgstr "Créer une nouvelle localisation (%(label)s)"
@ -5145,98 +4860,6 @@ msgstr "Brouillon"
msgid "Aucune version trouvée." msgid "Aucune version trouvée."
msgstr "Aucune version trouvée." msgstr "Aucune version trouvée."
#: assets/templates/assets/inspections_list.html:4
#: assets/templates/assets/inspections_list.html:15
msgid "Inspections d'assets"
msgstr "Historiques d'assets"
#: assets/templates/assets/inspections_list.html:17
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Suivi et historique des contrôles et vérifications sur le terrain"
#: assets/templates/assets/inspections_list.html:22
msgid "Nouvelle inspection (Carte GPS)"
msgstr "No interventions"
#: assets/templates/assets/inspections_list.html:32
msgid "Mes inspections"
msgstr "Dernière inspection"
#: assets/templates/assets/inspections_list.html:37
msgid "Toutes les inspections"
msgstr "Toutes les catégories"
#: assets/templates/assets/inspections_list.html:51
msgid "Rechercher par note, asset, ..."
msgstr "Rechercher"
#: assets/templates/assets/inspections_list.html:57
msgid "-- Thématiques --"
msgstr "Thématique"
#: assets/templates/assets/inspections_list.html:66
msgid "-- Natures --"
msgstr "Nature"
#: assets/templates/assets/inspections_list.html:75
msgid "-- Tous les résultats --"
msgstr "Nouveau"
#: assets/templates/assets/inspections_list.html:93
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspection trouvée"
msgstr[1] "%(counter)s inspections trouvées"
#: assets/templates/assets/inspections_list.html:94
msgid "limité aux 100 plus récentes"
msgstr "limité aux 100 plus récentes"
#: assets/templates/assets/inspections_list.html:104
#: assets/templates/assets/inspections_list.html:105
msgid "Carte des résultats"
msgstr "Date de déinstallation"
#: assets/templates/assets/inspections_list.html:148
msgid "Photos / Documents joints"
msgstr "Photos / Documents joints"
#: assets/templates/assets/inspections_list.html:167
#: assets/templates/assets/quick_inspection_map.html:333
msgid "Détail de l'inspection"
msgstr "Détail"
#: assets/templates/assets/inspections_list.html:176
#: assets/templates/assets/quick_inspection_map.html:341
msgid "Consulter la fiche de l'asset"
msgstr "Consulter la fiche de l'asset"
#: assets/templates/assets/inspections_list.html:183
#: assets/templates/assets/quick_inspection_map.html:347
msgid "Cible"
msgstr "Faible"
#: assets/templates/assets/inspections_list.html:222
msgid "Qualité du marquage"
msgstr "Qualité globale"
#: assets/templates/assets/inspections_list.html:238 assets/views/export.py:100
msgid "Obsolescence"
msgstr "Obsolescence"
#: assets/templates/assets/inspections_list.html:309
msgid "Aucune inspection trouvée"
msgstr "Aucune section trouvée"
#: assets/templates/assets/inspections_list.html:310
msgid "Aucune inspection ne correspond à vos critères de recherche."
msgstr "Aucune inspection ne correspond à vos critères de recherche."
#: assets/templates/assets/inspections_list.html:419
msgid "Carte des résultats d'inspections"
msgstr "Carte des résultats d'inspections"
#: assets/templates/assets/its_index.html:27 #: assets/templates/assets/its_index.html:27
msgid "Créer une nouvelle localisation ITS" msgid "Créer une nouvelle localisation ITS"
msgstr "Créer une nouvelle localisation ITS" msgstr "Créer une nouvelle localisation ITS"
@ -5799,102 +5422,14 @@ msgid "Poteaux"
msgstr "Poteaux" msgstr "Poteaux"
#: assets/templates/assets/publiclighting_street_detail.html:267 #: assets/templates/assets/publiclighting_street_detail.html:267
msgid "Alimentation:"
msgstr "Alimentation:"
#: assets/templates/assets/quick_inspection_map.html:4
msgid "Inspection Rapide Carte GPS"
msgstr "Inspection Rapide Carte GPS"
#: assets/templates/assets/quick_inspection_map.html:160
msgid "Inspection Rapide"
msgstr "Intersections"
#: assets/templates/assets/quick_inspection_map.html:161
msgid "GPS actif"
msgstr "GPS actif"
#: assets/templates/assets/quick_inspection_map.html:168
#: assets/templates/assets/unified_index.html:91 #: assets/templates/assets/unified_index.html:91
#: assets/templates/assets/unified_index.html:251 #: assets/templates/assets/unified_index.html:251
#: assets/templates/assets/unified_index.html:261 #: assets/templates/assets/unified_index.html:261
msgid "Alimentation:"
msgstr "Alimentation:"
msgid "Thématique" msgid "Thématique"
msgstr "Thématique" msgstr "Thématique"
#: assets/templates/assets/quick_inspection_map.html:182
msgid "Inspections (3 mois)"
msgstr "Intersections"
#: assets/templates/assets/quick_inspection_map.html:191
msgid "-- Thématique --"
msgstr "Thématique :"
#: assets/templates/assets/quick_inspection_map.html:198
msgid "-- Catégorie --"
msgstr "Catégorie"
#: assets/templates/assets/quick_inspection_map.html:206
msgid "Veuillez zoomer sur la carte pour afficher les équipements."
msgstr "Cliquez sur la carte pour ajouter des sommets."
#: assets/templates/assets/quick_inspection_map.html:216
msgid "Ma position GPS"
msgstr "Ma position GPS"
#: assets/templates/assets/quick_inspection_map.html:228
msgid "Choisir une thématique"
msgstr "Filtres thématiques"
#: assets/templates/assets/quick_inspection_map.html:260
msgid "Filtrer par catégorie"
msgstr "Filtrer par catégorie"
#: assets/templates/assets/quick_inspection_map.html:288
msgid "Historique des inspections sur la carte"
msgstr "Historique des modifications"
#: assets/templates/assets/quick_inspection_map.html:294
msgid ""
"Choisissez la durée d'affichage des contrôles et inspections effectués dans "
"la zone."
msgstr ""
#: assets/templates/assets/quick_inspection_map.html:298
msgid "Désactiver l'affichage"
msgstr "Désactiver l'affichage"
#: assets/templates/assets/quick_inspection_map.html:302
msgid "1 mois"
msgstr "1 mois"
#: assets/templates/assets/quick_inspection_map.html:306
msgid "3 mois (par défaut)"
msgstr "3 mois (par défaut)"
#: assets/templates/assets/quick_inspection_map.html:310
msgid "6 mois"
msgstr "6 mois"
#: assets/templates/assets/quick_inspection_map.html:314
msgid "1 an"
msgstr "1 an"
#: assets/templates/assets/quick_inspection_map.html:318
msgid "Tout (historique complet)"
msgstr "Tout (historique complet)"
#: assets/templates/assets/quick_inspection_map.html:370
msgid "Constats & Remarques"
msgstr "Constats & Remarques"
#: assets/templates/assets/quick_inspection_map.html:378
msgid "Galerie photos"
msgstr "Galerie photos"
#: assets/templates/assets/quick_inspection_map.html:397
msgid "Documents joints"
msgstr "Documents associés"
#: assets/templates/assets/roads_index.html:49 #: assets/templates/assets/roads_index.html:49
msgid "Qualité globale" msgid "Qualité globale"
msgstr "Qualité globale" msgstr "Qualité globale"
@ -7581,34 +7116,10 @@ msgstr "Format de date invalide. Utilisez ISO 8601."
msgid "Inexistant" msgid "Inexistant"
msgstr "Inexistant" msgstr "Inexistant"
#: assets/views/inspections.py:184
msgid "" msgid ""
"Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections." "Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections."
msgstr "" msgstr ""
#: assets/views/inspections.py:251
msgid "Cible d'inspection non spécifiée."
msgstr "Cible d'inspection non spécifiée."
#: assets/views/inspections.py:257
#, python-format
msgid "Cible introuvable : %s"
msgstr "Cible introuvable : %s"
#: assets/views/inspections.py:328
#, python-format
msgid "Intervention suite à inspection non conforme (%s)"
msgstr "Intervention suite à inspection non conforme (%s)"
#: assets/views/inspections.py:332
#, python-format
msgid "Signalement automatique lors de l'inspection du %s."
msgstr "Signalement automatique lors de l'inspection du %s."
#: assets/views/inspections.py:348
msgid "Inspection enregistrée avec succès."
msgstr "Commentaire enregistré avec succès"
#: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463 #: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463
#: assets/views/nature.py:543 assets/views/publiclighting.py:457 #: assets/views/nature.py:543 assets/views/publiclighting.py:457
#: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499 #: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499
@ -7751,3 +7262,4 @@ msgstr "Zones GIEP"
msgid "Aucune localisation GIEP trouvée à proximité" msgid "Aucune localisation GIEP trouvée à proximité"
msgstr "Aucune localisation trouvée" msgstr "Aucune localisation trouvée"

View file

@ -901,8 +901,7 @@ msgstr "Gepland"
msgid "Actif" msgid "Actif"
msgstr "Actief" msgstr "Actief"
#: assets/models/core.py:236 assets/models/inspection.py:15 #: assets/models/core.py:236
#: assets/templates/assets/components/inspection_form_modal.html:64
#: assets/templates/assets/detail_generic.html:1344 #: assets/templates/assets/detail_generic.html:1344
msgid "Endommagé" msgid "Endommagé"
msgstr "Beschadigd" msgstr "Beschadigd"
@ -924,21 +923,18 @@ msgstr "Verwijderd"
msgid "Archivé" msgid "Archivé"
msgstr "Gearchiveerd" msgstr "Gearchiveerd"
#: assets/models/core.py:244 assets/models/inspection.py:276 #: assets/models/core.py:244
#: assets/models/parking.py:32 #: assets/models/parking.py:32
#: assets/templates/assets/components/inspection_form_modal.html:128
msgid "Bon état" msgid "Bon état"
msgstr "Goede staat" msgstr "Goede staat"
#: assets/models/core.py:245 assets/models/inspection.py:277 #: assets/models/core.py:245
#: assets/models/parking.py:33 #: assets/models/parking.py:33
#: assets/templates/assets/components/inspection_form_modal.html:129
msgid "Moyen" msgid "Moyen"
msgstr "Gemiddeld" msgstr "Gemiddeld"
#: assets/models/core.py:246 assets/models/inspection.py:278 #: assets/models/core.py:246
#: assets/models/parking.py:34 #: assets/models/parking.py:34
#: assets/templates/assets/components/inspection_form_modal.html:130
msgid "Dégradé" msgid "Dégradé"
msgstr "Beschadigd" msgstr "Beschadigd"
@ -971,14 +967,12 @@ msgstr "Afgewezen"
msgid "À inspecter" msgid "À inspecter"
msgstr "Te inspecteren" msgstr "Te inspecteren"
#: assets/models/core.py:260 assets/models/inspection.py:12 #: assets/models/core.py:260
#: assets/templates/assets/components/inspection_form_modal.html:48
#: assets/views/core.py:852 #: assets/views/core.py:852
msgid "Conforme" msgid "Conforme"
msgstr "Normaal" msgstr "Normaal"
#: assets/models/core.py:261 #: assets/models/core.py:261
#: assets/templates/assets/components/inspection_form_modal.html:55
#: assets/views/core.py:853 #: assets/views/core.py:853
msgid "Non conforme" msgid "Non conforme"
msgstr "Niet conform" msgstr "Niet conform"
@ -997,7 +991,6 @@ msgstr "Financieringsprogramma / Project"
#: assets/models/history.py:186 #: assets/models/history.py:186
#: assets/templates/assets/buildings/publicbuilding_detail.html:302 #: assets/templates/assets/buildings/publicbuilding_detail.html:302
#: assets/templates/assets/inspections_list.html:187
#: assets/templates/assets/outdoor/outdoorlocation_detail.html:330 #: assets/templates/assets/outdoor/outdoorlocation_detail.html:330
msgid "Type d'asset" msgid "Type d'asset"
msgstr "Type asset" msgstr "Type asset"
@ -1183,7 +1176,6 @@ msgstr "Laatste meting"
#: assets/templates/assets/nature_index.html:82 #: assets/templates/assets/nature_index.html:82
#: assets/templates/assets/publiclighting_index.html:29 #: assets/templates/assets/publiclighting_index.html:29
#: assets/templates/assets/publiclighting_index.html:73 #: assets/templates/assets/publiclighting_index.html:73
#: assets/templates/assets/quick_inspection_map.html:175
#: assets/templates/assets/roads_index.html:29 #: assets/templates/assets/roads_index.html:29
#: assets/templates/assets/roads_index.html:85 #: assets/templates/assets/roads_index.html:85
#: assets/templates/assets/sign_index.html:29 #: assets/templates/assets/sign_index.html:29
@ -1266,190 +1258,14 @@ msgstr "Geometrie"
msgid "GeoJSON" msgid "GeoJSON"
msgstr "GeoJSON" msgstr "GeoJSON"
#: assets/models/inspection.py:13
msgid "Non conforme / Anomalie"
msgstr "Niet-conform / Anomalie"
#: assets/models/inspection.py:14
msgid "Données corrigées"
msgstr "Gecorrigeerde gegevens"
#: assets/models/inspection.py:16
msgid "Absent / Retiré"
msgstr "Afwezig / Teruggetrokken"
#: assets/models/inspection.py:20
msgid "Inspection visuelle"
msgstr "Kruispunt niet gevonden"
#: assets/models/inspection.py:21
msgid "Inspection technique"
msgstr "Technische inspectie"
#: assets/models/inspection.py:22
msgid "Suivi / Qualité post-intervention"
msgstr "Monitoring/kwaliteit na de interventie"
#: assets/models/inspection.py:26
msgid "Observer / Agent de terrain"
msgstr "Waarnemer / veldagent"
#: assets/models/inspection.py:27
msgid "Prestataire technique externe"
msgstr "Onderhoudsprovider controllers"
#: assets/models/inspection.py:28
msgid "Gestionnaire / Contrôleur interne"
msgstr "Leidinggevende / Interne Controller"
#: assets/models/inspection.py:39
msgid "Type d'asset / Cible"
msgstr "Te koppelen assettype"
#: assets/models/inspection.py:42
msgid "ID de la cible"
msgstr "Asset ID"
#: assets/models/inspection.py:49
msgid "Nature de l'inspection"
msgstr "Installatiedatum"
#: assets/models/inspection.py:55
msgid "Rôle de l'inspecteur"
msgstr "Rol van de inspecteur"
#: assets/models/inspection.py:64
msgid "Inspecteur / Vérificateur"
msgstr "Inspecteur / Verificateur"
#: assets/models/inspection.py:66
msgid "Date d'inspection"
msgstr "Installatiedatum"
#: assets/models/inspection.py:72
#: assets/templates/assets/components/asset_inspections.html:104 #: assets/templates/assets/components/asset_inspections.html:104
#: assets/templates/assets/components/inspection_form_modal.html:41
#: assets/templates/assets/inspections_list.html:195
#: assets/templates/assets/quick_inspection_map.html:355
msgid "Résultat du contrôle" msgid "Résultat du contrôle"
msgstr "Controlecentrum" msgstr "Controlecentrum"
#: assets/models/inspection.py:79 #: assets/models/parking.py:35
msgid "État d'obsolescence constaté"
msgstr "Staat van veroudering opgemerkt"
#: assets/models/inspection.py:82
msgid "Remarques / Constats"
msgstr "Opmerking over de grond"
#: assets/models/inspection.py:85
msgid "Appliquer directement les modifications à l'asset"
msgstr "Pas wijzigingen rechtstreeks op het item toe"
#: assets/models/inspection.py:92
msgid "Inspection d'asset (Générique)"
msgstr "Inspectie van activa (algemeen)"
#: assets/models/inspection.py:93
msgid "Inspections d'assets (Génériques)"
msgstr "Inspecties van activa (algemeen)"
#: assets/models/inspection.py:196
#, python-format
msgid "%(count)d arceaux classiques"
msgstr "%(count)d klassieke hoepels"
#: assets/models/inspection.py:198
#, python-format
msgid "%(count)d arceaux cargo"
msgstr "%(count)d vrachthoepels"
#: assets/models/inspection.py:200
msgid "Drop-zone trottinette"
msgstr "Scooter met dropzone"
#: assets/models/inspection.py:200
msgid "Sans drop-zone"
msgstr "Zonder dropzone"
#: assets/models/inspection.py:203
msgid "Marquage bon état"
msgstr "Goede staat markering"
#: assets/models/inspection.py:204
msgid "Marquage moyen"
msgstr "Merk"
#: assets/models/inspection.py:205
msgid "Marquage dégradé"
msgstr "Verslechterde markering"
#: assets/models/inspection.py:206
msgid "Marquage absent / effacé"
msgstr "Markering ontbreekt/gewist"
#: assets/models/inspection.py:207
msgid "Marquage mauvais"
msgstr "Slechte markering"
#: assets/models/inspection.py:213
#, python-format
msgid "Obsolescence : %s"
msgstr "Veroudering : %s"
#: assets/models/inspection.py:257
msgid "Nombre d'arceaux classiques constaté"
msgstr "Aantal klassieke hoepels waargenomen"
#: assets/models/inspection.py:262
msgid "Nombre d'arceaux cargo constaté"
msgstr "Aantal waargenomen vrachthoepels"
#: assets/models/inspection.py:268
msgid "Type d'arceau constaté"
msgstr "Sleuteltype"
#: assets/models/inspection.py:273
msgid "Drop zone trottinette constatée"
msgstr "Drop-scooterzone genoteerd"
#: assets/models/inspection.py:279 assets/models/parking.py:35
#: assets/templates/assets/components/inspection_form_modal.html:131
msgid "Absent / Effacé" msgid "Absent / Effacé"
msgstr "Afwezig / verwijderd" msgstr "Afwezig / verwijderd"
#: assets/models/inspection.py:286
msgid "Qualité du marquage constatée"
msgstr "Kwaliteit van de markering waargenomen"
#: assets/models/inspection.py:290
msgid "Mettre à jour la capacité / comptages de l'asset"
msgstr "De asset aanmaken"
#: assets/models/inspection.py:294
msgid "Inspection d'emplacement de stationnement"
msgstr "Inspectie van parkeerterreinen"
#: assets/models/inspection.py:295
msgid "Inspections d'emplacements de stationnement"
msgstr "Inspecties van parkeerterreinen"
#: assets/models/inspection.py:333
msgid "Inspection"
msgstr "Kruispunten"
#: assets/models/inspection.py:338
msgid "Fichier / Photo / Document"
msgstr "Bestand / Foto / Document"
#: assets/models/inspection.py:343
msgid "Document / Photo d'inspection"
msgstr "Keuringsdocument/foto"
#: assets/models/inspection.py:344
msgid "Documents / Photos d'inspection"
msgstr "Inspectiedocumenten/foto's"
#: assets/models/nature.py:81 #: assets/models/nature.py:81
msgid "Tulipes" msgid "Tulipes"
msgstr "Tulpen" msgstr "Tulpen"
@ -2917,7 +2733,6 @@ msgstr "U hebt geen toestemming om assets voor dit thema te bewerken"
#: assets/templates/assets/clean_index.html:8 #: assets/templates/assets/clean_index.html:8
#: assets/templates/assets/controlcenters_index.html:16 #: assets/templates/assets/controlcenters_index.html:16
#: assets/templates/assets/controlcenters_index.html:45 #: assets/templates/assets/controlcenters_index.html:45
#: assets/templates/assets/inspections_list.html:83
#: assets/templates/assets/its_index.html:16 #: assets/templates/assets/its_index.html:16
#: assets/templates/assets/its_index.html:56 #: assets/templates/assets/its_index.html:56
#: assets/templates/assets/nature_index.html:28 #: assets/templates/assets/nature_index.html:28
@ -2944,7 +2759,6 @@ msgstr "Filteren"
#: assets/templates/assets/clean_index.html:9 #: assets/templates/assets/clean_index.html:9
#: assets/templates/assets/controlcenters_index.html:17 #: assets/templates/assets/controlcenters_index.html:17
#: assets/templates/assets/controlcenters_index.html:46 #: assets/templates/assets/controlcenters_index.html:46
#: assets/templates/assets/inspections_list.html:84
#: assets/templates/assets/its_index.html:17 #: assets/templates/assets/its_index.html:17
#: assets/templates/assets/its_index.html:57 #: assets/templates/assets/its_index.html:57
#: assets/templates/assets/nature_index.html:29 #: assets/templates/assets/nature_index.html:29
@ -3218,16 +3032,12 @@ msgstr "Totalen van assets"
#: assets/templates/assets/components/asset_interventions.html:112 #: assets/templates/assets/components/asset_interventions.html:112
#: assets/templates/assets/components/asset_location_link.html:50 #: assets/templates/assets/components/asset_location_link.html:50
#: assets/templates/assets/components/asset_model_card.html:62 #: assets/templates/assets/components/asset_model_card.html:62
#: assets/templates/assets/components/inspection_form_modal.html:11
#: assets/templates/assets/components/inspection_form_modal.html:172
#: assets/templates/assets/components/inspection_form_modal.html:204
#: assets/templates/assets/components/location_edit_modals.html:24 #: assets/templates/assets/components/location_edit_modals.html:24
#: assets/templates/assets/components/location_edit_modals.html:56 #: assets/templates/assets/components/location_edit_modals.html:56
#: assets/templates/assets/components/location_edit_modals.html:82 #: assets/templates/assets/components/location_edit_modals.html:82
#: assets/templates/assets/controlcenters_location_detail.html:112 #: assets/templates/assets/controlcenters_location_detail.html:112
#: assets/templates/assets/controlcenters_location_detail.html:145 #: assets/templates/assets/controlcenters_location_detail.html:145
#: assets/templates/assets/controlcenters_location_detail.html:170 #: assets/templates/assets/controlcenters_location_detail.html:170
#: assets/templates/assets/inspections_list.html:299
#: assets/templates/assets/its_location_detail.html:55 #: assets/templates/assets/its_location_detail.html:55
#: assets/templates/assets/its_location_detail.html:88 #: assets/templates/assets/its_location_detail.html:88
#: assets/templates/assets/naturerwiz_detail.html:273 #: assets/templates/assets/naturerwiz_detail.html:273
@ -3283,7 +3093,6 @@ msgstr "Kies exportformaat:"
#: assets/templates/assets/components/asset_interventions.html:173 #: assets/templates/assets/components/asset_interventions.html:173
#: assets/templates/assets/components/asset_location_link.html:75 #: assets/templates/assets/components/asset_location_link.html:75
#: assets/templates/assets/components/asset_model_card.html:98 #: assets/templates/assets/components/asset_model_card.html:98
#: assets/templates/assets/components/inspection_form_modal.html:152
#: assets/templates/assets/components/location_edit_modals.html:38 #: assets/templates/assets/components/location_edit_modals.html:38
#: assets/templates/assets/components/location_edit_modals.html:63 #: assets/templates/assets/components/location_edit_modals.html:63
#: assets/templates/assets/components/location_edit_modals.html:96 #: assets/templates/assets/components/location_edit_modals.html:96
@ -4225,8 +4034,6 @@ msgstr "Laatste inspectie"
#: assets/templates/assets/components/asset_inspections.html:33 #: assets/templates/assets/components/asset_inspections.html:33
#: assets/templates/assets/components/asset_inspections.html:100 #: assets/templates/assets/components/asset_inspections.html:100
#: assets/templates/assets/inspections_list.html:191
#: assets/templates/assets/quick_inspection_map.html:351
msgid "Date & Heure" msgid "Date & Heure"
msgstr "Datum / tijd" msgstr "Datum / tijd"
@ -4236,21 +4043,16 @@ msgstr "Controlecentrum"
#: assets/templates/assets/components/asset_inspections.html:35 #: assets/templates/assets/components/asset_inspections.html:35
#: assets/templates/assets/components/asset_inspections.html:108 #: assets/templates/assets/components/asset_inspections.html:108
#: assets/templates/assets/inspections_list.html:199
#: assets/templates/assets/quick_inspection_map.html:359
#: assets/views/locations.py:28 #: assets/views/locations.py:28
msgid "Nature" msgid "Nature"
msgstr "Natuur" msgstr "Natuur"
#: assets/templates/assets/components/asset_inspections.html:36 #: assets/templates/assets/components/asset_inspections.html:36
#: assets/templates/assets/components/asset_inspections.html:112 #: assets/templates/assets/components/asset_inspections.html:112
#: assets/templates/assets/inspections_list.html:203
#: assets/templates/assets/quick_inspection_map.html:363
msgid "Inspecteur" msgid "Inspecteur"
msgstr "Detector" msgstr "Detector"
#: assets/templates/assets/components/asset_inspections.html:37 #: assets/templates/assets/components/asset_inspections.html:37
#: assets/templates/assets/inspections_list.html:231
msgid "Remarques" msgid "Remarques"
msgstr "Opmerking" msgstr "Opmerking"
@ -4267,12 +4069,10 @@ msgid "Équipement cible"
msgstr "Uitrusting" msgstr "Uitrusting"
#: assets/templates/assets/components/asset_inspections.html:119 #: assets/templates/assets/components/asset_inspections.html:119
#: assets/templates/assets/inspections_list.html:210
msgid "Arceaux classiques constatés" msgid "Arceaux classiques constatés"
msgstr "Vastgestelde klassieke beugels" msgstr "Vastgestelde klassieke beugels"
#: assets/templates/assets/components/asset_inspections.html:125 #: assets/templates/assets/components/asset_inspections.html:125
#: assets/templates/assets/inspections_list.html:216
msgid "Arceaux cargo constatés" msgid "Arceaux cargo constatés"
msgstr "Vastgestelde cargobeugels" msgstr "Vastgestelde cargobeugels"
@ -4281,7 +4081,6 @@ msgid "Remarques / Observations"
msgstr "Observaties" msgstr "Observaties"
#: assets/templates/assets/components/asset_inspections.html:143 #: assets/templates/assets/components/asset_inspections.html:143
#: assets/templates/assets/inspections_list.html:246
msgid "Photos & Documents joints" msgid "Photos & Documents joints"
msgstr "Bijgevoegde foto's & documenten" msgstr "Bijgevoegde foto's & documenten"
@ -4462,8 +4261,6 @@ msgid "Modèle actuel :"
msgstr "Huidig model:" msgstr "Huidig model:"
#: assets/templates/assets/components/asset_model_card.html:77 #: assets/templates/assets/components/asset_model_card.html:77
#: assets/templates/assets/quick_inspection_map.html:266
#: assets/templates/assets/quick_inspection_map.html:267
msgid "Toutes les catégories" msgid "Toutes les catégories"
msgstr "Alle categorieën" msgstr "Alle categorieën"
@ -4475,92 +4272,10 @@ msgstr "Nieuw model"
msgid "Sélectionner un modèle..." msgid "Sélectionner un modèle..."
msgstr "Selecteer een model" msgstr "Selecteer een model"
#: assets/templates/assets/components/inspection_form_modal.html:9
msgid "Nouvelle inspection"
msgstr "Nieuwe interventie"
#: assets/templates/assets/components/inspection_form_modal.html:31
msgid "Statut actuel"
msgstr "Status"
#: assets/templates/assets/components/inspection_form_modal.html:42
msgid "Résultat"
msgstr "Resultaat"
#: assets/templates/assets/components/inspection_form_modal.html:71
msgid "Absent"
msgstr "Afwezig"
#: assets/templates/assets/components/inspection_form_modal.html:78
msgid "Corrigé"
msgstr "Gecorrigeerd"
#: assets/templates/assets/components/inspection_form_modal.html:88
msgid "Photos & Fichiers"
msgstr "Bestanden kiezen"
#: assets/templates/assets/components/inspection_form_modal.html:94
msgid "Prendre des photos (mode continu)"
msgstr "Foto's maken (continue modus)"
#: assets/templates/assets/components/inspection_form_modal.html:98
msgid "Photos, vidéos, fichiers"
msgstr "Bestanden kiezen"
#: assets/templates/assets/components/inspection_form_modal.html:113
msgid "Contrôles spécifiques stationnement"
msgstr "Specifieke parkeercontroles"
#: assets/templates/assets/components/inspection_form_modal.html:116
msgid "Arceaux classiques"
msgstr "Klassieke leunbeugels"
#: assets/templates/assets/components/inspection_form_modal.html:120
msgid "Arceaux cargo"
msgstr "Cargofiets leunbeugels"
#: assets/templates/assets/components/inspection_form_modal.html:125
msgid "Qualité marquage"
msgstr "Globale kwaliteit"
#: assets/templates/assets/components/inspection_form_modal.html:127
msgid "-- Sélectionner --"
msgstr "Selecteren"
#: assets/templates/assets/components/inspection_form_modal.html:140
msgid "Créer automatiquement une intervention en cas d'anomalie / dégât"
msgstr "Automatisch een interventie aanmaken bij anomalie / schade"
#: assets/templates/assets/components/inspection_form_modal.html:146
msgid "Remarques / Remarques du constat"
msgstr "Opmerkingen / Vaststellingsopmerkingen"
#: assets/templates/assets/components/inspection_form_modal.html:147
msgid "Saisir vos remarques ou détails..."
msgstr "Voer uw opmerkingen of details in..."
#: assets/templates/assets/components/inspection_form_modal.html:156
msgid "Valider l'inspection"
msgstr "Selectie bevestigen"
#: assets/templates/assets/components/inspection_form_modal.html:170
msgid "Caméra (Mode continu)"
msgstr "Contractnummer"
#: assets/templates/assets/components/inspection_form_modal.html:185
#: assets/templates/assets/components/map_polygon_picker.html:37 #: assets/templates/assets/components/map_polygon_picker.html:37
msgid "Terminer" msgid "Terminer"
msgstr "Voltooien" msgstr "Voltooien"
#: assets/templates/assets/components/inspection_form_modal.html:188
msgid "Capturer"
msgstr "Natuur"
#: assets/templates/assets/components/inspection_form_modal.html:202
msgid "Choisir un équipement à inspecter"
msgstr "Andere technische uitrusting"
#: assets/templates/assets/components/inspection_form_modal.html:207
msgid "" msgid ""
"Plusieurs équipements ont été détectés à cet emplacement. Veuillez " "Plusieurs équipements ont été détectés à cet emplacement. Veuillez "
"sélectionner celui à inspecter :" "sélectionner celui à inspecter :"
@ -4630,8 +4345,8 @@ msgstr ""
msgid "Supprimer" msgid "Supprimer"
msgstr "Verwijderen" msgstr "Verwijderen"
# , python-format
#: assets/templates/assets/components/location_edit_modals.html:80 #: assets/templates/assets/components/location_edit_modals.html:80
# , python-format
#, python-format #, python-format
msgid "Créer une nouvelle localisation (%(label)s)" msgid "Créer une nouvelle localisation (%(label)s)"
msgstr "Nieuwe locatie aanmaken (%(label)s)" msgstr "Nieuwe locatie aanmaken (%(label)s)"
@ -5137,98 +4852,10 @@ msgstr "Concept"
msgid "Aucune version trouvée." msgid "Aucune version trouvée."
msgstr "Geen versie gevonden." msgstr "Geen versie gevonden."
#: assets/templates/assets/inspections_list.html:4 #: assets/views/export.py:100
#: assets/templates/assets/inspections_list.html:15
msgid "Inspections d'assets"
msgstr "Asset geschiedenissen"
#: assets/templates/assets/inspections_list.html:17
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Monitoring en geschiedenis van controles en verificaties in het veld"
#: assets/templates/assets/inspections_list.html:22
msgid "Nouvelle inspection (Carte GPS)"
msgstr "Nieuwe interventie"
#: assets/templates/assets/inspections_list.html:32
msgid "Mes inspections"
msgstr "Laatste inspectie"
#: assets/templates/assets/inspections_list.html:37
msgid "Toutes les inspections"
msgstr "Alle categorieën"
#: assets/templates/assets/inspections_list.html:51
msgid "Rechercher par note, asset, ..."
msgstr "Zoeken"
#: assets/templates/assets/inspections_list.html:57
msgid "-- Thématiques --"
msgstr "Thema"
#: assets/templates/assets/inspections_list.html:66
msgid "-- Natures --"
msgstr "Natuur"
#: assets/templates/assets/inspections_list.html:75
msgid "-- Tous les résultats --"
msgstr "Alle statussen"
#: assets/templates/assets/inspections_list.html:93
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspectie gevonden"
msgstr[1] "%(counter)s inspecties gevonden"
#: assets/templates/assets/inspections_list.html:94
msgid "limité aux 100 plus récentes"
msgstr "beperkt tot de 100 meest recente"
#: assets/templates/assets/inspections_list.html:104
#: assets/templates/assets/inspections_list.html:105
msgid "Carte des résultats"
msgstr "Startdatum"
#: assets/templates/assets/inspections_list.html:148
msgid "Photos / Documents joints"
msgstr "Foto's / Bijgevoegde documenten"
#: assets/templates/assets/inspections_list.html:167
#: assets/templates/assets/quick_inspection_map.html:333
msgid "Détail de l'inspection"
msgstr "Detail van "
#: assets/templates/assets/inspections_list.html:176
#: assets/templates/assets/quick_inspection_map.html:341
msgid "Consulter la fiche de l'asset"
msgstr "Bekijk assetkaart"
#: assets/templates/assets/inspections_list.html:183
#: assets/templates/assets/quick_inspection_map.html:347
msgid "Cible"
msgstr "Laag"
#: assets/templates/assets/inspections_list.html:222
msgid "Qualité du marquage"
msgstr "Globale kwaliteit"
#: assets/templates/assets/inspections_list.html:238 assets/views/export.py:100
msgid "Obsolescence" msgid "Obsolescence"
msgstr "Veroudering" msgstr "Veroudering"
#: assets/templates/assets/inspections_list.html:309
msgid "Aucune inspection trouvée"
msgstr "Geen versie gevonden."
#: assets/templates/assets/inspections_list.html:310
msgid "Aucune inspection ne correspond à vos critères de recherche."
msgstr "Er zijn geen inspecties die overeenkomen met uw zoekcriteria."
#: assets/templates/assets/inspections_list.html:419
msgid "Carte des résultats d'inspections"
msgstr "Kaart met inspectieresultaten"
#: assets/templates/assets/its_index.html:27 #: assets/templates/assets/its_index.html:27
msgid "Créer une nouvelle localisation ITS" msgid "Créer une nouvelle localisation ITS"
msgstr "Nieuwe ITS-locatie aanmaken" msgstr "Nieuwe ITS-locatie aanmaken"
@ -5784,99 +5411,17 @@ msgstr "Palen"
msgid "Alimentation:" msgid "Alimentation:"
msgstr "Voeding:" msgstr "Voeding:"
#: assets/templates/assets/quick_inspection_map.html:4
msgid "Inspection Rapide Carte GPS"
msgstr "Snelle inspectie GPS-kaart"
#: assets/templates/assets/quick_inspection_map.html:160
msgid "Inspection Rapide"
msgstr "Kruispunten"
#: assets/templates/assets/quick_inspection_map.html:161
msgid "GPS actif"
msgstr "Inactief"
#: assets/templates/assets/quick_inspection_map.html:168
#: assets/templates/assets/unified_index.html:91 #: assets/templates/assets/unified_index.html:91
#: assets/templates/assets/unified_index.html:251 #: assets/templates/assets/unified_index.html:251
#: assets/templates/assets/unified_index.html:261 #: assets/templates/assets/unified_index.html:261
msgid "Thématique" msgid "Thématique"
msgstr "Thema" msgstr "Thema"
#: assets/templates/assets/quick_inspection_map.html:182
msgid "Inspections (3 mois)"
msgstr "Kruispunten"
#: assets/templates/assets/quick_inspection_map.html:191
msgid "-- Thématique --"
msgstr "Thema:"
#: assets/templates/assets/quick_inspection_map.html:198
msgid "-- Catégorie --"
msgstr "Categorie"
#: assets/templates/assets/quick_inspection_map.html:206
msgid "Veuillez zoomer sur la carte pour afficher les équipements."
msgstr "Klik op de kaart om hoekpunten toe te voegen."
#: assets/templates/assets/quick_inspection_map.html:216
msgid "Ma position GPS"
msgstr "Mijn GPS-locatie"
#: assets/templates/assets/quick_inspection_map.html:228
msgid "Choisir une thématique"
msgstr "Thematische filters"
#: assets/templates/assets/quick_inspection_map.html:260
msgid "Filtrer par catégorie"
msgstr "Selecteer een categorie"
#: assets/templates/assets/quick_inspection_map.html:288
msgid "Historique des inspections sur la carte"
msgstr "Wijzigingsgeschiedenis"
#: assets/templates/assets/quick_inspection_map.html:294
msgid "" msgid ""
"Choisissez la durée d'affichage des contrôles et inspections effectués dans " "Choisissez la durée d'affichage des contrôles et inspections effectués dans "
"la zone." "la zone."
msgstr "" msgstr ""
#: assets/templates/assets/quick_inspection_map.html:298
msgid "Désactiver l'affichage"
msgstr "Uitgeschakeld"
#: assets/templates/assets/quick_inspection_map.html:302
msgid "1 mois"
msgstr "maanden"
#: assets/templates/assets/quick_inspection_map.html:306
msgid "3 mois (par défaut)"
msgstr "3 maanden (standaard)"
#: assets/templates/assets/quick_inspection_map.html:310
msgid "6 mois"
msgstr "maanden"
#: assets/templates/assets/quick_inspection_map.html:314
msgid "1 an"
msgstr "1 jaar"
#: assets/templates/assets/quick_inspection_map.html:318
msgid "Tout (historique complet)"
msgstr "Alles (volledige geschiedenis)"
#: assets/templates/assets/quick_inspection_map.html:370
msgid "Constats & Remarques"
msgstr "Vaststellingen & Opmerkingen"
#: assets/templates/assets/quick_inspection_map.html:378
msgid "Galerie photos"
msgstr "Foto's vuilnisbakken"
#: assets/templates/assets/quick_inspection_map.html:397
msgid "Documents joints"
msgstr "Gekoppelde documenten"
#: assets/templates/assets/roads_index.html:49 #: assets/templates/assets/roads_index.html:49
msgid "Qualité globale" msgid "Qualité globale"
msgstr "Globale kwaliteit" msgstr "Globale kwaliteit"
@ -7555,34 +7100,10 @@ msgstr "Ongeldig datumformaat. Gebruik ISO 8601."
msgid "Inexistant" msgid "Inexistant"
msgstr "Niet bestaand" msgstr "Niet bestaand"
#: assets/views/inspections.py:184
msgid "" msgid ""
"Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections." "Vous ne disposez d'aucune thématique autorisée pour réaliser des inspections."
msgstr "" msgstr ""
#: assets/views/inspections.py:251
msgid "Cible d'inspection non spécifiée."
msgstr "Inspectiedoel niet gespecificeerd."
#: assets/views/inspections.py:257
#, python-format
msgid "Cible introuvable : %s"
msgstr "Doel niet gevonden : %s"
#: assets/views/inspections.py:328
#, python-format
msgid "Intervention suite à inspection non conforme (%s)"
msgstr "Interventie na niet-conforme inspectie (%s)"
#: assets/views/inspections.py:332
#, python-format
msgid "Signalement automatique lors de l'inspection du %s."
msgstr "Automatische rapportage bij het inspecteren van %s."
#: assets/views/inspections.py:348
msgid "Inspection enregistrée avec succès."
msgstr "Opmerking succesvol opgeslagen"
#: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463 #: assets/views/its.py:404 assets/views/its.py:415 assets/views/nature.py:463
#: assets/views/nature.py:543 assets/views/publiclighting.py:457 #: assets/views/nature.py:543 assets/views/publiclighting.py:457
#: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499 #: assets/views/publiclighting.py:478 assets/views/publiclighting.py:499
@ -7725,3 +7246,4 @@ msgstr "GIEP-zones"
msgid "Aucune localisation GIEP trouvée à proximité" msgid "Aucune localisation GIEP trouvée à proximité"
msgstr "Geen locatie gevonden." msgstr "Geen locatie gevonden."

View file

@ -1,6 +1,6 @@
# Generated by Django 6.0.7 on 2026-08-07 15:40 # Generated by Django 6.0.7 on 2026-08-07 15:40
import assets.models.inspection import inspections.models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -21,7 +21,7 @@ class Migration(migrations.Migration):
name='InspectionDocument', name='InspectionDocument',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(upload_to=assets.models.inspection.inspection_document_upload_path, verbose_name='Fichier / Photo / Document')), ('file', models.FileField(upload_to=inspections.models.inspection_document_upload_path, verbose_name='Fichier / Photo / Document')),
('created_at', models.DateTimeField(auto_now_add=True)), ('created_at', models.DateTimeField(auto_now_add=True)),
('inspection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='assets.assetinspection', verbose_name='Inspection')), ('inspection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='assets.assetinspection', verbose_name='Inspection')),
], ],

View file

@ -1,6 +1,6 @@
# Generated by Django 6.0.7 on 2026-08-07 15:46 # Generated by Django 6.0.7 on 2026-08-07 15:46
import assets.models.inspection import inspections.models
import common.storages import common.storages
from django.db import migrations, models from django.db import migrations, models
@ -15,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='inspectiondocument', model_name='inspectiondocument',
name='file', name='file',
field=models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=assets.models.inspection.inspection_document_upload_path, verbose_name='Fichier / Photo / Document'), field=models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=inspections.models.inspection_document_upload_path, verbose_name='Fichier / Photo / Document'),
), ),
] ]

View file

@ -13,4 +13,4 @@ from .clean import CleanLocation, CleanLitterBinModel, CleanLitterBin, CleanLitt
from .buildings import PublicBuilding, BuildingAsset, BuildingEquipment, PublicBuildingDocument from .buildings import PublicBuilding, BuildingAsset, BuildingEquipment, PublicBuildingDocument
from .outdoor import OutdoorLocation, OutdoorAsset, OutdoorLocationDocument from .outdoor import OutdoorLocation, OutdoorAsset, OutdoorLocationDocument
from .parking import ParkingLocation, ParkingSpot from .parking import ParkingLocation, ParkingSpot
from .inspection import AssetInspection, ParkingSpotInspection from inspections.models import Inspection as AssetInspection, ParkingSpotInspection

View file

@ -292,7 +292,7 @@ class AbstractAsset(models.Model):
asset_inspections = GenericRelation( asset_inspections = GenericRelation(
'assets.AssetInspection', 'inspections.Inspection',
content_type_field='asset_content_type', content_type_field='asset_content_type',
object_id_field='asset_object_id', object_id_field='asset_object_id',
related_query_name='%(app_label)s_%(class)s' related_query_name='%(app_label)s_%(class)s'

View file

@ -193,7 +193,6 @@
'non_compliant', '#fd7e14', 'non_compliant', '#fd7e14',
'damaged', '#dc3545', 'damaged', '#dc3545',
'missing', '#212529', 'missing', '#212529',
'updated', '#0dcaf0',
'#6c757d' '#6c757d'
], ],
'circle-stroke-width': 2, 'circle-stroke-width': 2,
@ -675,7 +674,7 @@
filter_geom: getMapBbox() filter_geom: getMapBbox()
}; };
fetch('/assets/inspections/geojson/', { fetch('/inspections/api/geojson/', {
method: 'POST', method: 'POST',
body: JSON.stringify(payload), body: JSON.stringify(payload),
headers: { headers: {

View file

@ -46,7 +46,7 @@
</td> </td>
<td> <td>
<span class="badge {{ insp.status_badge_class }}"> <span class="badge {{ insp.status_badge_class }}">
{{ insp.get_result_status_display }} <i class="bi {{ insp.status_icon }} me-1"></i>{{ insp.get_result_status_display }}
</span> </span>
</td> </td>
<td> <td>
@ -102,7 +102,7 @@
</li> </li>
<li class="list-group-item d-flex justify-content-between align-items-center px-0"> <li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-shield-check me-1"></i>{% translate "Résultat du contrôle" %}</span> <span class="text-muted"><i class="bi bi-shield-check me-1"></i>{% translate "Résultat du contrôle" %}</span>
<span class="badge {{ insp.status_badge_class }}">{{ insp.get_result_status_display }}</span> <span class="badge {{ insp.status_badge_class }}"><i class="bi {{ insp.status_icon }} me-1"></i>{{ insp.get_result_status_display }}</span>
</li> </li>
<li class="list-group-item d-flex justify-content-between align-items-center px-0"> <li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-eye me-1"></i>{% translate "Nature" %}</span> <span class="text-muted"><i class="bi bi-eye me-1"></i>{% translate "Nature" %}</span>
@ -144,7 +144,7 @@
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
{% for doc in insp.documents.all %} {% for doc in insp.documents.all %}
<a href="{{ doc.file_url }}" target="_blank" class="d-inline-block border rounded p-1 bg-light"> <a href="{{ doc.file_url }}" target="_blank" class="d-inline-block border rounded p-1 bg-light">
<img src="{{ doc.file_url }}" alt="Photo" class="rounded" style="width: 75px; height: 75px; object-fit: cover;"> <img data-src="{{ doc.file_url }}" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3C/svg%3E" loading="lazy" alt="Photo" class="rounded" style="width: 75px; height: 75px; object-fit: cover;">
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
@ -160,7 +160,7 @@
{% endfor %} {% endfor %}
{% if can_inspect %} {% if can_inspect %}
{% include "assets/components/inspection_form_modal.html" %} {% include "inspections/components/inspection_form_modal.html" %}
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {

View file

@ -513,11 +513,11 @@ def asset_inspections(context, asset):
} }
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from assets.models import AssetInspection from inspections.models import Inspection
from assets.permissions import can_inspect_asset from assets.permissions import can_inspect_asset
asset_ct = ContentType.objects.get_for_model(asset) asset_ct = ContentType.objects.get_for_model(asset)
inspections = list(AssetInspection.objects.filter( inspections = list(Inspection.objects.filter(
asset_content_type=asset_ct, asset_content_type=asset_ct,
asset_object_id=asset.pk asset_object_id=asset.pk
).select_related('inspector').prefetch_related('documents').order_by('-inspection_date')) ).select_related('inspector').prefetch_related('documents').order_by('-inspection_date'))

View file

@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from assets.models.parking import ParkingSpot from assets.models.parking import ParkingSpot
from assets.models.inspection import AssetInspection, ParkingSpotInspection from inspections.models import Inspection as AssetInspection, ParkingSpotInspection
from interventions.models import Intervention from interventions.models import Intervention
User = get_user_model() User = get_user_model()
@ -50,12 +50,12 @@ class InspectionViewsTestCase(TestCase):
nature='visual' nature='visual'
) )
response = self.client.get(reverse('assets:inspections_list')) response = self.client.get(reverse('inspections:inspections_list'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, 'PK-001') self.assertContains(response, 'PK-001')
def test_quick_inspection_map_view(self): def test_quick_inspection_map_view(self):
response = self.client.get(reverse('assets:quick_inspection_map')) response = self.client.get(reverse('inspections:quick_inspection_map'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_quick_inspection_map_mobile_view(self): def test_quick_inspection_map_mobile_view(self):
@ -63,7 +63,7 @@ class InspectionViewsTestCase(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_create_inspection_api_compliant(self): def test_create_inspection_api_compliant(self):
response = self.client.post(reverse('assets:create_inspection_api'), { response = self.client.post(reverse('inspections:create_inspection_api'), {
'content_type_id': self.spot_ct.id, 'content_type_id': self.spot_ct.id,
'object_id': self.spot.id, 'object_id': self.spot.id,
'result_status': 'compliant', 'result_status': 'compliant',
@ -80,7 +80,7 @@ class InspectionViewsTestCase(TestCase):
self.assertTrue(self.spot.is_compliant) self.assertTrue(self.spot.is_compliant)
def test_create_inspection_api_damaged_creates_intervention(self): def test_create_inspection_api_damaged_creates_intervention(self):
response = self.client.post(reverse('assets:create_inspection_api'), { response = self.client.post(reverse('inspections:create_inspection_api'), {
'content_type_id': self.spot_ct.id, 'content_type_id': self.spot_ct.id,
'object_id': self.spot.id, 'object_id': self.spot.id,
'result_status': 'damaged', 'result_status': 'damaged',
@ -112,7 +112,7 @@ class InspectionViewsTestCase(TestCase):
UserThematics.objects.create(user_config=user_config, thematic=t_other, can_inspect=False) UserThematics.objects.create(user_config=user_config, thematic=t_other, can_inspect=False)
self.client.force_login(user_perm) self.client.force_login(user_perm)
response = self.client.get(reverse('assets:quick_inspection_map')) response = self.client.get(reverse('inspections:quick_inspection_map'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, 'parking') self.assertContains(response, 'parking')
self.assertNotContains(response, 'publiclighting') self.assertNotContains(response, 'publiclighting')
@ -129,12 +129,12 @@ class InspectionViewsTestCase(TestCase):
self.client.force_login(user_no_inspect) self.client.force_login(user_no_inspect)
# 1. Page liste des inspections -> Bouton 'Nouvelle inspection' masqué # 1. Page liste des inspections -> Bouton 'Nouvelle inspection' masqué
resp_list = self.client.get(reverse('assets:inspections_list')) resp_list = self.client.get(reverse('inspections:inspections_list'))
self.assertEqual(resp_list.status_code, 200) self.assertEqual(resp_list.status_code, 200)
self.assertNotContains(resp_list, reverse('assets:quick_inspection_map')) self.assertNotContains(resp_list, reverse('inspections:quick_inspection_map'))
# 2. Tentative d'accès direct à quick_inspection_map -> HTTP 403 Forbidden # 2. Tentative d'accès direct à quick_inspection_map -> HTTP 403 Forbidden
resp_map = self.client.get(reverse('assets:quick_inspection_map')) resp_map = self.client.get(reverse('inspections:quick_inspection_map'))
self.assertEqual(resp_map.status_code, 403) self.assertEqual(resp_map.status_code, 403)
self.client.force_login(self.user) self.client.force_login(self.user)
@ -142,7 +142,7 @@ class InspectionViewsTestCase(TestCase):
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
test_photo = SimpleUploadedFile("test_inspection.jpg", b"fake_image_bytes", content_type="image/jpeg") test_photo = SimpleUploadedFile("test_inspection.jpg", b"fake_image_bytes", content_type="image/jpeg")
response = self.client.post(reverse('assets:create_inspection_api'), { response = self.client.post(reverse('inspections:create_inspection_api'), {
'content_type_id': self.spot_ct.id, 'content_type_id': self.spot_ct.id,
'object_id': self.spot.id, 'object_id': self.spot.id,
'result_status': 'compliant', 'result_status': 'compliant',
@ -176,7 +176,7 @@ class InspectionViewsTestCase(TestCase):
import json import json
response = self.client.post( response = self.client.post(
reverse('assets:inspections_geojson'), reverse('inspections:inspections_geojson'),
data=json.dumps({'thematic': 'parking', 'period': '3m'}), data=json.dumps({'thematic': 'parking', 'period': '3m'}),
content_type='application/json' content_type='application/json'
) )
@ -198,7 +198,7 @@ class InspectionViewsTestCase(TestCase):
nature='visual' nature='visual'
) )
response = self.client.get(reverse('assets:inspections_list') + '?tab=all&q=SEARCH-99') response = self.client.get(reverse('inspections:inspections_list') + '?tab=all&q=SEARCH-99')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn(insp, response.context['inspections']) self.assertIn(insp, response.context['inspections'])
@ -213,7 +213,7 @@ class InspectionViewsTestCase(TestCase):
UserThematics.objects.create(user_config=user_config, thematic=t_lighting, can_view_assets=False, can_inspect=False) UserThematics.objects.create(user_config=user_config, thematic=t_lighting, can_view_assets=False, can_inspect=False)
self.client.force_login(user_custom) self.client.force_login(user_custom)
resp = self.client.get(reverse('assets:inspections_list')) resp = self.client.get(reverse('inspections:inspections_list'))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn(t_parking, resp.context['thematics']) self.assertIn(t_parking, resp.context['thematics'])
self.assertNotIn(t_lighting, resp.context['thematics']) self.assertNotIn(t_lighting, resp.context['thematics'])
@ -221,7 +221,7 @@ class InspectionViewsTestCase(TestCase):
self.client.force_login(self.user) self.client.force_login(self.user)
def test_user_without_config_returns_none(self): def test_user_without_config_returns_none(self):
from assets.views.inspections import get_user_viewable_thematics, get_user_inspectable_thematics from inspections.views import get_user_viewable_thematics, get_user_inspectable_thematics
user_no_config, _ = User.objects.get_or_create(username='no_config_user', defaults={'password': 'pwd'}) user_no_config, _ = User.objects.get_or_create(username='no_config_user', defaults={'password': 'pwd'})
from common.models import UserConfig from common.models import UserConfig
UserConfig.objects.filter(user=user_no_config).delete() UserConfig.objects.filter(user=user_no_config).delete()
@ -240,7 +240,7 @@ class InspectionViewsTestCase(TestCase):
nature='visual' nature='visual'
) )
request = self.client.get(reverse('assets:inspections_list')).wsgi_request request = self.client.get(reverse('inspections:inspections_list')).wsgi_request
dummy_context = {'request': request} dummy_context = {'request': request}
data = asset_inspections(dummy_context, self.spot) data = asset_inspections(dummy_context, self.spot)
self.assertEqual(data['total_count'], 1) self.assertEqual(data['total_count'], 1)

View file

@ -1,14 +1,9 @@
from django.urls import path from django.urls import path
from .views import core, locations, nature, publiclighting, trafficlights, artworks, structures, controlcenters, sign, roads, its, clean, asset_editing, history, location_editing, export, asset_permissions, water, buildings, outdoor, parking
from .views import core, locations, nature, publiclighting, trafficlights, artworks, structures, controlcenters, sign, roads, its, clean, asset_editing, history, location_editing, export, asset_permissions, water, buildings, outdoor, parking, inspections
app_name = "assets" app_name = "assets"
urlpatterns = [ urlpatterns = [
path("", core.unified_assets_index, name="index"), path("", core.unified_assets_index, name="index"),
path("inspections/", inspections.inspections_list, name="inspections_list"),
path("inspections/new/", inspections.quick_inspection_map, name="quick_inspection_map"),
path("inspections/create/", inspections.create_inspection_api, name="create_inspection_api"),
path("inspections/geojson/", inspections.inspections_geojson, name="inspections_geojson"),
path("parking/", parking.parking_index, name="parking_index"), path("parking/", parking.parking_index, name="parking_index"),
path("parking/assets/", parking.parking_assets, name="parking_assets"), path("parking/assets/", parking.parking_assets, name="parking_assets"),
path("parking/assets/geojson/", parking.parking_assets_geojson, name="parking_assets_geojson"), path("parking/assets/geojson/", parking.parking_assets_geojson, name="parking_assets_geojson"),

View file

@ -342,8 +342,8 @@ def parking_assets_detail(request, asset_model, asset_id):
spot = get_object_or_404(ParkingSpot, id=asset_id) spot = get_object_or_404(ParkingSpot, id=asset_id)
content_type = ContentType.objects.get_for_model(ParkingSpot) content_type = ContentType.objects.get_for_model(ParkingSpot)
from assets.models.inspection import AssetInspection from inspections.models import Inspection
inspections = AssetInspection.objects.filter( inspections = Inspection.objects.filter(
asset_content_type=content_type, asset_content_type=content_type,
asset_object_id=spot.id asset_object_id=spot.id
).order_by('-inspection_date') ).order_by('-inspection_date')

View file

@ -0,0 +1 @@
# Django app for Inspections

View file

@ -0,0 +1,47 @@
from django.contrib import admin
from .models import Inspection, ParkingSpotInspection, InspectionDocument
class InspectionDocumentInline(admin.TabularInline):
model = InspectionDocument
extra = 0
@admin.register(Inspection)
class InspectionAdmin(admin.ModelAdmin):
list_display = (
"id", "asset", "inspector", "inspection_date",
"result_status", "mission_intervention", "checked_intervention",
"apply_changes_to_asset"
)
list_filter = (
"result_status", "nature", "inspector_role",
"apply_changes_to_asset", "inspection_date"
)
search_fields = (
"notes", "inspector__username", "inspector__first_name",
"inspector__last_name", "mission_intervention__name",
"checked_intervention__name"
)
raw_id_fields = ("inspector", "mission_intervention", "checked_intervention")
inlines = [InspectionDocumentInline]
@admin.register(ParkingSpotInspection)
class ParkingSpotInspectionAdmin(admin.ModelAdmin):
list_display = (
"id", "asset", "inspector", "inspection_date",
"result_status", "verified_standard_stand_count",
"verified_cargo_stand_count", "verified_has_scooter_drop_zone",
"apply_changes_to_asset"
)
list_filter = ("result_status", "apply_changes_to_asset", "inspection_date")
search_fields = ("notes", "inspector__username", "inspector__first_name", "inspector__last_name")
raw_id_fields = ("inspector", "mission_intervention", "checked_intervention")
inlines = [InspectionDocumentInline]
@admin.register(InspectionDocument)
class InspectionDocumentAdmin(admin.ModelAdmin):
list_display = ("id", "inspection", "filename", "created_at")
raw_id_fields = ("inspection",)

View file

@ -0,0 +1,22 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class InspectionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'inspections'
verbose_name = _("Inspections & Contrôles")
def ready(self):
try:
from common.private_files.registry import register
def guard_inspection_document(obj, user):
if not user.is_authenticated:
return False
return True
register('inspections', 'InspectionDocument', 'file', guard_inspection_document)
register('assets', 'InspectionDocument', 'file', guard_inspection_document)
except Exception:
pass

View file

@ -0,0 +1,7 @@
from inspections.models import Inspection, INSPECTION_RESULT_CONFIG
def inspection_constants(request):
return {
'INSPECTION_RESULT_CONFIG': INSPECTION_RESULT_CONFIG,
'INSPECTION_RESULT_CHOICES_CONFIG': Inspection.get_result_choices_config(),
}

Binary file not shown.

View file

@ -0,0 +1,106 @@
msgid ""
msgstr ""
"Project-Id-Version: StreetUp Inspections 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-08 14:25+0200\n"
"PO-Revision-Date: 2026-08-08 14:25+0200\n"
"Last-Translator: \n"
"Language-Team: English\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Inspections & Contrôles"
msgstr "Inspections & Controls"
msgid "Historique des inspections"
msgstr "Inspections History"
msgid "Inspections d'assets"
msgstr "Asset Inspections"
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Field checks and inspection history"
msgid "Nouvelle inspection (Carte GPS)"
msgstr "New inspection (GPS Map)"
msgid "Mes inspections"
msgstr "My inspections"
msgid "Toutes les inspections"
msgstr "All inspections"
msgid "Rechercher par note, asset, ..."
msgstr "Search by note, asset, ..."
msgid "-- Thématiques --"
msgstr "-- Thematics --"
msgid "-- Natures --"
msgstr "-- Natures --"
msgid "-- Tous les résultats --"
msgstr "-- All results --"
msgid "Résultat de l'inspection"
msgstr "Inspection result"
msgid "Résultat du contrôle"
msgstr "Inspection result"
msgid "Résultat"
msgstr "Result"
msgid "Conforme"
msgstr "Compliant"
msgid "Non conforme / Anomalie"
msgstr "Non compliant / Anomaly"
msgid "Non conforme"
msgstr "Non-compliant"
msgid "Adapté / Corrigé"
msgstr "Adapted / Corrected"
msgid "Corrigé"
msgstr "Corrected"
msgid "Endommagé"
msgstr "Damaged"
msgid "Absent / Retiré"
msgstr "Missing / Removed"
msgid "Absent"
msgstr "Absent"
msgid "Statut actuel"
msgstr "Current status"
msgid "Inspection visuelle"
msgstr "Visual inspection"
msgid "Inspection technique"
msgstr "Technical inspection"
msgid "Suivi / Qualité post-intervention"
msgstr "Post-intervention quality check"
msgid "Observer / Agent de terrain"
msgstr "Observer / Field agent"
msgid "Prestataire technique externe"
msgstr "External technical contractor"
msgid "Gestionnaire / Contrôleur interne"
msgstr "Manager / Internal controller"
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspection found"
msgstr[1] "%(counter)s inspections found"

Binary file not shown.

View file

@ -0,0 +1,106 @@
msgid ""
msgstr ""
"Project-Id-Version: StreetUp Inspections 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-08 14:25+0200\n"
"PO-Revision-Date: 2026-08-08 14:25+0200\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "Inspections & Contrôles"
msgstr "Inspections & Contrôles"
msgid "Historique des inspections"
msgstr "Historique des inspections"
msgid "Inspections d'assets"
msgstr "Historique des inspections"
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Suivi et historique des contrôles et vérifications sur le terrain"
msgid "Nouvelle inspection (Carte GPS)"
msgstr "Nouvelle inspection (Carte GPS)"
msgid "Mes inspections"
msgstr "Mes inspections"
msgid "Toutes les inspections"
msgstr "Toutes les inspections"
msgid "Rechercher par note, asset, ..."
msgstr "Rechercher par note, asset, ..."
msgid "-- Thématiques --"
msgstr "-- Thématiques --"
msgid "-- Natures --"
msgstr "-- Natures --"
msgid "-- Tous les résultats --"
msgstr "-- Tous les résultats --"
msgid "Résultat de l'inspection"
msgstr "Résultat de l'inspection"
msgid "Résultat du contrôle"
msgstr "Résultat du contrôle"
msgid "Résultat"
msgstr "Résultat"
msgid "Conforme"
msgstr "Conforme"
msgid "Non conforme / Anomalie"
msgstr "Non conforme / Anomalie"
msgid "Non conforme"
msgstr "Non conforme"
msgid "Adapté / Corrigé"
msgstr "Adapté / Corrigé"
msgid "Corrigé"
msgstr "Corrigé"
msgid "Endommagé"
msgstr "Endommagé"
msgid "Absent / Retiré"
msgstr "Absent / Retiré"
msgid "Absent"
msgstr "Absent"
msgid "Statut actuel"
msgstr "Statut actuel"
msgid "Inspection visuelle"
msgstr "Inspection visuelle"
msgid "Inspection technique"
msgstr "Inspection technique"
msgid "Suivi / Qualité post-intervention"
msgstr "Suivi / Qualité post-intervention"
msgid "Observer / Agent de terrain"
msgstr "Observer / Agent de terrain"
msgid "Prestataire technique externe"
msgstr "Prestataire technique externe"
msgid "Gestionnaire / Contrôleur interne"
msgstr "Gestionnaire / Contrôleur interne"
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspection trouvée"
msgstr[1] "%(counter)s inspections trouvées"

Binary file not shown.

View file

@ -0,0 +1,106 @@
msgid ""
msgstr ""
"Project-Id-Version: StreetUp Inspections 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-08 14:25+0200\n"
"PO-Revision-Date: 2026-08-08 14:25+0200\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Inspections & Contrôles"
msgstr "Inspecties & Controles"
msgid "Historique des inspections"
msgstr "Inspectiegeschiedenis"
msgid "Inspections d'assets"
msgstr "Assetinspecties"
msgid "Suivi et historique des contrôles et vérifications sur le terrain"
msgstr "Opvolging en geschiedenis van terreincontroles"
msgid "Nouvelle inspection (Carte GPS)"
msgstr "Nieuwe inspectie (GPS-kaart)"
msgid "Mes inspections"
msgstr "Mijn inspecties"
msgid "Toutes les inspections"
msgstr "Alle inspecties"
msgid "Rechercher par note, asset, ..."
msgstr "Zoeken op opmerking, asset, ..."
msgid "-- Thématiques --"
msgstr "-- Thematieken --"
msgid "-- Natures --"
msgstr "-- Aard --"
msgid "-- Tous les résultats --"
msgstr "-- Alle resultaten --"
msgid "Résultat de l'inspection"
msgstr "Inspectieresultaat"
msgid "Résultat du contrôle"
msgstr "Controlleresultaat"
msgid "Résultat"
msgstr "Resultaat"
msgid "Conforme"
msgstr "Conform"
msgid "Non conforme / Anomalie"
msgstr "Niet conform / Anomalie"
msgid "Non conforme"
msgstr "Niet conform"
msgid "Adapté / Corrigé"
msgstr "Aangepast / Gecorrigeerd"
msgid "Corrigé"
msgstr "Gecorrigeerd"
msgid "Endommagé"
msgstr "Beschadigd"
msgid "Absent / Retiré"
msgstr "Afwezig / Verwijderd"
msgid "Absent"
msgstr "Afwezig"
msgid "Statut actuel"
msgstr "Huidige status"
msgid "Inspection visuelle"
msgstr "Visuele inspectie"
msgid "Inspection technique"
msgstr "Technische inspectie"
msgid "Suivi / Qualité post-intervention"
msgstr "Kwaliteitscontrole na interventie"
msgid "Observer / Agent de terrain"
msgstr "Waarnemer / Terreinagent"
msgid "Prestataire technique externe"
msgstr "Externe technische aannemer"
msgid "Gestionnaire / Contrôleur interne"
msgstr "Beheerder / Interne controleur"
#, python-format
msgid "%(counter)s inspection trouvée"
msgid_plural "%(counter)s inspections trouvées"
msgstr[0] "%(counter)s inspectie gevonden"
msgstr[1] "%(counter)s inspecties gevonden"

View file

@ -0,0 +1,77 @@
# Generated by Django 6.0.7 on 2026-08-08 10:47
import common.storages
import django.db.models.deletion
import django.utils.timezone
import inspections.models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('interventions', '0002_remove_intervention_origin_fms_nbr_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Inspection',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('asset_object_id', models.PositiveIntegerField(verbose_name='ID de la cible')),
('nature', models.CharField(choices=[('visual', 'Inspection visuelle'), ('technical', 'Inspection technique'), ('post_intervention', 'Suivi / Qualité post-intervention')], default='visual', max_length=50, verbose_name="Nature de l'inspection")),
('inspector_role', models.CharField(choices=[('observer', 'Observer / Agent de terrain'), ('provider', 'Prestataire technique externe'), ('manager', 'Gestionnaire / Contrôleur interne')], default='observer', max_length=50, verbose_name="Rôle de l'inspecteur")),
('inspection_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name="Date d'inspection")),
('result_status', models.CharField(choices=[('compliant', 'Conforme'), ('non_compliant', 'Non conforme / Anomalie'), ('updated', 'Données corrigées'), ('damaged', 'Endommagé'), ('missing', 'Absent / Retiré')], default='compliant', max_length=50, verbose_name='Résultat du contrôle')),
('verified_obsolescence', models.CharField(blank=True, max_length=50, null=True, verbose_name="État d'obsolescence constaté")),
('notes', models.TextField(blank=True, null=True, verbose_name='Remarques / Constats')),
('apply_changes_to_asset', models.BooleanField(default=True, verbose_name='Appliquer directement les modifications à la cible')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('asset_content_type', models.ForeignKey(limit_choices_to={'app_label__in': ['assets', 'interventions']}, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name="Type d'asset / Cible")),
('checked_intervention', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='quality_inspections', to='interventions.intervention', verbose_name='Intervention de travaux contrôlée')),
('inspector', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inspections', to=settings.AUTH_USER_MODEL, verbose_name='Inspecteur / Vérificateur')),
('mission_intervention', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='generated_inspections', to='interventions.intervention', verbose_name="Intervention / Mission d'inspection")),
],
options={
'verbose_name': 'Inspection (Générique)',
'verbose_name_plural': 'Inspections (Génériques)',
'ordering': ['-inspection_date'],
},
),
migrations.CreateModel(
name='ParkingSpotInspection',
fields=[
('inspection_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='inspections.inspection')),
('verified_standard_stand_count', models.IntegerField(blank=True, null=True, verbose_name="Nombre d'arceaux classiques constaté")),
('verified_cargo_stand_count', models.IntegerField(blank=True, null=True, verbose_name="Nombre d'arceaux cargo constaté")),
('verified_stand_type', models.CharField(blank=True, max_length=50, null=True, verbose_name="Type d'arceau constaté")),
('verified_has_scooter_drop_zone', models.BooleanField(blank=True, null=True, verbose_name='Drop zone trottinette constatée')),
('verified_marking_quality', models.CharField(blank=True, choices=[('good', 'Bon état'), ('medium', 'Moyen'), ('damaged', 'Dégradé'), ('missing', 'Absent / Effacé')], max_length=50, null=True, verbose_name='Qualité du marquage constatée')),
('apply_physical_counts_to_asset', models.BooleanField(default=False, verbose_name="Mettre à jour la capacité / comptages de l'asset")),
],
options={
'verbose_name': "Inspection d'emplacement de stationnement",
'verbose_name_plural': "Inspections d'emplacements de stationnement",
},
bases=('inspections.inspection',),
),
migrations.CreateModel(
name='InspectionDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(storage=common.storages.PrivateMediaStorage(), upload_to=inspections.models.inspection_document_upload_path, verbose_name='Fichier / Photo / Document')),
('created_at', models.DateTimeField(auto_now_add=True)),
('inspection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='inspections.inspection', verbose_name='Inspection')),
],
options={
'verbose_name': "Document / Photo d'inspection",
'verbose_name_plural': "Documents / Photos d'inspection",
},
),
]

View file

@ -0,0 +1,75 @@
from django.db import migrations
def copy_inspections(apps, schema_editor):
connection = schema_editor.connection
with connection.cursor() as cursor:
table_names = connection.introspection.table_names(cursor)
if 'assets_assetinspection' in table_names:
cursor.execute("""
INSERT INTO inspections_inspection (
id, asset_object_id, nature, inspector_role, inspection_date,
result_status, verified_obsolescence, notes, apply_changes_to_asset,
created_at, updated_at, asset_content_type_id, inspector_id,
mission_intervention_id, checked_intervention_id
)
SELECT
id, asset_object_id, nature, inspector_role, inspection_date,
result_status, verified_obsolescence, notes, apply_changes_to_asset,
created_at, updated_at, asset_content_type_id, inspector_id,
NULL, NULL
FROM assets_assetinspection
ON CONFLICT (id) DO NOTHING;
""")
if connection.vendor == 'postgresql':
cursor.execute("""
SELECT setval(pg_get_serial_sequence('inspections_inspection', 'id'), COALESCE(MAX(id), 1))
FROM inspections_inspection;
""")
if 'assets_parkingspotinspection' in table_names:
cursor.execute("""
INSERT INTO inspections_parkingspotinspection (
inspection_ptr_id, verified_standard_stand_count, verified_cargo_stand_count,
verified_stand_type, verified_has_scooter_drop_zone, verified_marking_quality,
apply_physical_counts_to_asset
)
SELECT
assetinspection_ptr_id, verified_standard_stand_count, verified_cargo_stand_count,
verified_stand_type, verified_has_scooter_drop_zone, verified_marking_quality,
apply_physical_counts_to_asset
FROM assets_parkingspotinspection
ON CONFLICT (inspection_ptr_id) DO NOTHING;
""")
if 'assets_inspectiondocument' in table_names:
cursor.execute("""
INSERT INTO inspections_inspectiondocument (
id, file, created_at, inspection_id
)
SELECT
id, file, created_at, inspection_id
FROM assets_inspectiondocument
ON CONFLICT (id) DO NOTHING;
""")
if connection.vendor == 'postgresql':
cursor.execute("""
SELECT setval(pg_get_serial_sequence('inspections_inspectiondocument', 'id'), COALESCE(MAX(id), 1))
FROM inspections_inspectiondocument;
""")
def reverse_copy_inspections(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('inspections', '0001_initial'),
]
operations = [
migrations.RunPython(copy_inspections, reverse_copy_inspections),
]

View file

@ -8,12 +8,39 @@ from common.storages import PrivateMediaStorage
private_storage = PrivateMediaStorage() private_storage = PrivateMediaStorage()
INSPECTION_RESULT_CHOICES = ( INSPECTION_RESULT_CONFIG = {
('compliant', _('Conforme')), 'compliant': {
('non_compliant', _('Non conforme / Anomalie')), 'label': _('Conforme'),
('updated', _('Données corrigées')), 'badge_class': 'bg-success',
('damaged', _('Endommagé')), 'btn_class': 'btn-outline-success',
('missing', _('Absent / Retiré')), 'icon': 'bi-check-circle-fill',
'color_hex': '#198754',
},
'non_compliant': {
'label': _('Non conforme / Anomalie'),
'badge_class': 'bg-warning text-dark',
'btn_class': 'btn-outline-warning text-dark',
'icon': 'bi-exclamation-triangle-fill',
'color_hex': '#ffc107',
},
'damaged': {
'label': _('Endommagé'),
'badge_class': 'bg-danger',
'btn_class': 'btn-outline-danger',
'icon': 'bi-x-circle-fill',
'color_hex': '#dc3545',
},
'missing': {
'label': _('Absent / Retiré'),
'badge_class': 'bg-dark',
'btn_class': 'btn-outline-dark',
'icon': 'bi-dash-circle-fill',
'color_hex': '#212529',
},
}
INSPECTION_RESULT_CHOICES = tuple(
(code, cfg['label']) for code, cfg in INSPECTION_RESULT_CONFIG.items()
) )
INSPECTION_NATURE_CHOICES = ( INSPECTION_NATURE_CHOICES = (
@ -29,7 +56,7 @@ INSPECTOR_ROLE_CHOICES = (
) )
class AssetInspection(models.Model): class Inspection(models.Model):
""" """
Modèle d'enregistrement générique d'une vérification/inspection sur le terrain d'une cible (Asset, Localisation, Intervention). Modèle d'enregistrement générique d'une vérification/inspection sur le terrain d'une cible (Asset, Localisation, Intervention).
""" """
@ -42,6 +69,24 @@ class AssetInspection(models.Model):
asset_object_id = models.PositiveIntegerField(verbose_name=_("ID de la cible")) asset_object_id = models.PositiveIntegerField(verbose_name=_("ID de la cible"))
asset = GenericForeignKey('asset_content_type', 'asset_object_id') asset = GenericForeignKey('asset_content_type', 'asset_object_id')
# Liaisons optionnelles avec les Interventions
mission_intervention = models.ForeignKey(
'interventions.Intervention',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='generated_inspections',
verbose_name=_("Intervention / Mission d'inspection")
)
checked_intervention = models.ForeignKey(
'interventions.Intervention',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='quality_inspections',
verbose_name=_("Intervention de travaux contrôlée")
)
nature = models.CharField( nature = models.CharField(
max_length=50, max_length=50,
choices=INSPECTION_NATURE_CHOICES, choices=INSPECTION_NATURE_CHOICES,
@ -60,7 +105,7 @@ class AssetInspection(models.Model):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
blank=True, blank=True,
related_name='asset_inspections', related_name='inspections',
verbose_name=_("Inspecteur / Vérificateur") verbose_name=_("Inspecteur / Vérificateur")
) )
inspection_date = models.DateTimeField(default=timezone.now, verbose_name=_("Date d'inspection")) inspection_date = models.DateTimeField(default=timezone.now, verbose_name=_("Date d'inspection"))
@ -82,17 +127,25 @@ class AssetInspection(models.Model):
notes = models.TextField(null=True, blank=True, verbose_name=_("Remarques / Constats")) notes = models.TextField(null=True, blank=True, verbose_name=_("Remarques / Constats"))
apply_changes_to_asset = models.BooleanField( apply_changes_to_asset = models.BooleanField(
default=True, default=True,
verbose_name=_("Appliquer directement les modifications à l'asset") verbose_name=_("Appliquer directement les modifications à la cible")
) )
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
class Meta: class Meta:
verbose_name = _("Inspection d'asset (Générique)") verbose_name = _("Inspection (Générique)")
verbose_name_plural = _("Inspections d'assets (Génériques)") verbose_name_plural = _("Inspections (Génériques)")
ordering = ['-inspection_date'] ordering = ['-inspection_date']
@property
def is_mission_inspection(self):
return self.mission_intervention_id is not None
@property
def is_cross_check(self):
return self.checked_intervention_id is not None
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
@ -132,14 +185,27 @@ class AssetInspection(models.Model):
@property @property
def status_badge_class(self): def status_badge_class(self):
mapping = { return INSPECTION_RESULT_CONFIG.get(self.result_status, {}).get('badge_class', 'bg-secondary')
'compliant': 'bg-success',
'non_compliant': 'bg-warning text-dark', @property
'damaged': 'bg-danger', def status_btn_class(self):
'missing': 'bg-dark', return INSPECTION_RESULT_CONFIG.get(self.result_status, {}).get('btn_class', 'btn-outline-secondary')
'updated': 'bg-info text-dark',
} @property
return mapping.get(self.result_status, 'bg-secondary') def status_icon(self):
return INSPECTION_RESULT_CONFIG.get(self.result_status, {}).get('icon', 'bi-question-circle')
@property
def status_color_hex(self):
return INSPECTION_RESULT_CONFIG.get(self.result_status, {}).get('color_hex', '#6c757d')
@classmethod
def get_result_choices_config(cls):
"""Retourne la liste des configurations pour chaque statut de résultat (code, label, badge_class, btn_class, icon, color_hex)."""
return [
{'code': code, **cfg}
for code, cfg in INSPECTION_RESULT_CONFIG.items()
]
@property @property
def target_display(self): def target_display(self):
@ -242,14 +308,18 @@ class AssetInspection(models.Model):
return self.get_asset_url(is_mobile=True) return self.get_asset_url(is_mobile=True)
def __str__(self): def __str__(self):
asset_str = str(self.asset) if self.asset else f"Asset #{self.asset_object_id}" asset_str = str(self.asset) if self.asset else f"Cible #{self.asset_object_id}"
date_str = self.inspection_date.strftime('%d/%m/%Y') if self.inspection_date else '' date_str = self.inspection_date.strftime('%d/%m/%Y') if self.inspection_date else ''
return f"Inspection {asset_str} ({date_str}) - {self.get_result_status_display()}" return f"Inspection {asset_str} ({date_str}) - {self.get_result_status_display()}"
class ParkingSpotInspection(AssetInspection): # Alias de compatibilité
AssetInspection = Inspection
class ParkingSpotInspection(Inspection):
""" """
Spécialisation de l'inspection pour les emplacements de stationnement (vélos, trottinettes, livraison, PMR). Spécialisation de l'inspection pour les emplacements de stationnement.
""" """
verified_standard_stand_count = models.IntegerField( verified_standard_stand_count = models.IntegerField(
null=True, null=True,
@ -324,10 +394,10 @@ def inspection_document_upload_path(instance, filename):
class InspectionDocument(models.Model): class InspectionDocument(models.Model):
""" """
Document / Photo / Pièce jointe associée à une inspection d'asset. Document / Photo / Pièce jointe associée à une inspection.
""" """
inspection = models.ForeignKey( inspection = models.ForeignKey(
AssetInspection, Inspection,
related_name='documents', related_name='documents',
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_("Inspection") verbose_name=_("Inspection")
@ -360,7 +430,7 @@ class InspectionDocument(models.Model):
if not self.file: if not self.file:
return "" return ""
from common.private_files.helpers import build_private_url from common.private_files.helpers import build_private_url
return build_private_url('assets', 'InspectionDocument', self.pk, 'file') return build_private_url('inspections', 'InspectionDocument', self.pk, 'file')
def __str__(self): def __str__(self):
return f"Document {self.filename} (Inspection #{self.inspection_id})" return f"Document {self.filename} (Inspection #{self.inspection_id})"

View file

@ -0,0 +1,184 @@
{% load i18n %}
<!-- Modale de Saisie Rapide d'Inspection -->
<div class="modal fade" id="quickInspectionModal" tabindex="-1" aria-labelledby="quickInspectionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content shadow">
<div class="modal-header bg-light">
<h5 class="modal-title fw-bold" id="quickInspectionModalLabel">
<i class="bi bi-search text-primary me-2"></i>{% translate "Nouvelle inspection" %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
</div>
<form id="quickInspectionForm" action="{% url 'inspections:create_inspection_api' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="content_type_id" id="insp_content_type_id">
<input type="hidden" name="object_id" id="insp_object_id">
<input type="hidden" name="nature" value="visual">
<input type="hidden" name="inspector_role" value="observer">
<div class="modal-body">
<!-- Target Card Preview -->
<div class="card mb-3 bg-light border-0">
<div class="card-body p-2 d-flex align-items-center justify-content-between">
<div>
<span class="badge bg-secondary mb-1" id="insp_target_type">Asset</span>
<h6 class="mb-0 fw-bold" id="insp_target_code">#12345</h6>
<small class="text-muted" id="insp_target_name">-</small>
</div>
<div class="text-end">
<span class="small text-muted d-block">{% translate "Statut actuel" %}</span>
<span class="badge bg-info text-dark" id="insp_target_status">-</span>
</div>
</div>
</div>
<div id="quickInspectionAlert" class="alert alert-danger d-none mb-3"></div>
<!-- Résultat de contrôle avec boutons colorés -->
<div class="mb-3">
<label class="form-label fw-semibold d-block">{% translate "Résultat de l'inspection" %} <span class="text-danger">*</span></label>
<div class="d-flex flex-column gap-2" role="group" aria-label="{% translate 'Résultat' %}">
<div class="row g-2">
{% for choice in INSPECTION_RESULT_CHOICES_CONFIG %}
<div class="col-6 mb-1">
<input type="radio" class="btn-check" name="result_status" id="res_{{ choice.code }}" value="{{ choice.code }}" {% if forloop.first %}checked{% endif %}>
<label class="btn {{ choice.btn_class }} w-100 py-2 d-flex align-items-center justify-content-center gap-1" for="res_{{ choice.code }}">
<i class="bi {{ choice.icon }}"></i>
<span>{{ choice.label }}</span>
</label>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Section Photos (Mode continu & Fichiers) -->
<div class="card border border-success-subtle mb-3" style="background-color: #e8f6f3;">
<div class="card-header bg-transparent border-0 pb-0 pt-2 px-3">
<span class="fw-bold text-dark small"><i class="bi bi-camera me-1 text-teal"></i>{% translate "Photos & Fichiers" %}</span>
</div>
<div class="card-body p-3">
<div class="d-grid gap-2">
<button type="button" id="insp_btn_continuous_camera" class="btn btn-outline-secondary bg-white text-dark py-2 d-flex align-items-center justify-content-center gap-2 shadow-sm" style="border-color: #ced4da;">
<i class="bi bi-camera me-1"></i>
<span>{% translate "Prendre des photos (mode continu)" %}</span>
</button>
<button type="button" id="insp_btn_gallery_files" class="btn btn-outline-secondary bg-white text-dark py-2 d-flex align-items-center justify-content-center gap-2 shadow-sm" style="border-color: #ced4da;">
<i class="bi bi-folder2-open me-1"></i>
<span>{% translate "Photos, vidéos, fichiers" %}</span>
</button>
</div>
<!-- Inputs masqués -->
<input type="file" id="insp_camera_input" accept="image/*" capture="environment" class="d-none">
<input type="file" id="insp_gallery_input" accept="image/*,.pdf,.doc,.docx,.xls,.xlsx" multiple class="d-none">
<!-- Grille de prévisualisation des photos/fichiers accumulés -->
<div id="insp_photos_preview_container" class="row g-2 mt-2"></div>
</div>
</div>
<!-- Champs spécifiques ParkingSpot (masqués par défaut) -->
<div id="parkingSpotSpecificFields" class="d-none border rounded p-2 mb-3 bg-white">
<span class="small fw-bold text-secondary mb-2 d-block"><i class="bi bi-p-square me-1"></i>{% translate "Contrôles spécifiques stationnement" %}</span>
<div class="row g-2 mb-2">
<div class="col-6">
<label class="form-label small mb-1">{% translate "Arceaux classiques" %}</label>
<input type="number" name="verified_standard_stand_count" id="insp_std_stands" class="form-control form-control-sm" min="0">
</div>
<div class="col-6">
<label class="form-label small mb-1">{% translate "Arceaux cargo" %}</label>
<input type="number" name="verified_cargo_stand_count" id="insp_cargo_stands" class="form-control form-control-sm" min="0">
</div>
</div>
<div class="mb-1">
<label class="form-label small mb-1">{% translate "Qualité marquage" %}</label>
<select name="verified_marking_quality" id="insp_marking_quality" class="form-select form-select-sm">
<option value="">{% translate "-- Sélectionner --" %}</option>
<option value="good">{% translate "Bon état" %}</option>
<option value="medium">{% translate "Moyen" %}</option>
<option value="damaged">{% translate "Dégradé" %}</option>
<option value="missing">{% translate "Absent / Effacé" %}</option>
</select>
</div>
</div>
<!-- Option Création automatique d'une intervention -->
<div class="form-check form-switch mb-3 bg-light p-2 rounded ps-4" id="createInterventionSwitchContainer">
<input class="form-check-input ms-0 me-2" type="checkbox" name="create_intervention" value="true" id="createInterventionSwitch">
<label class="form-check-label small fw-semibold" for="createInterventionSwitch">
{% translate "Créer automatiquement une intervention en cas d'anomalie / dégât" %}
</label>
</div>
<!-- Remarques -->
<div class="mb-3">
<label class="form-label fw-semibold mb-1">{% translate "Remarques / Remarques du constat" %}</label>
<textarea name="notes" id="insp_notes" class="form-control" rows="2" placeholder="{% translate 'Saisir vos remarques ou détails...' %}"></textarea>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
<button type="submit" class="btn btn-primary" id="submitQuickInspectionBtn">
<span class="spinner-border spinner-border-sm me-1 d-none" id="submitQuickInspectionSpinner"></span>
<i class="bi bi-check-lg me-1" id="submitQuickInspectionIcon"></i>
{% translate "Valider l'inspection" %}
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal caméra Canvas (mode continu pour inspection) -->
<div class="modal fade" id="inspCameraModal" tabindex="-1" aria-labelledby="inspCameraModalLabel" aria-hidden="true" style="z-index: 1060;">
<div class="modal-dialog modal-fullscreen-sm-down d-flex m-0 h-100 mx-auto">
<div class="modal-content bg-dark text-white d-flex flex-column">
<div class="modal-header border-0 flex-shrink-0 py-2">
<h5 class="modal-title fw-bold" id="inspCameraModalLabel">
<i class="bi bi-camera-video me-1"></i>{% translate "Caméra (Mode continu)" %}
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}" id="inspCameraModalCloseBtn"></button>
</div>
<div class="modal-body d-flex flex-column p-2 flex-grow-1" style="overflow: hidden; min-height: 0;">
<div class="flex-grow-1 d-flex align-items-center justify-content-center" style="min-height: 0; overflow: hidden;">
<div class="position-relative w-100 h-100 d-flex align-items-center justify-content-center" style="border-radius: 0.5rem; overflow: hidden;">
<video id="inspCameraPreview" playsinline autoplay style="max-width: 100%; max-height: 100%; object-fit: contain;"></video>
<div id="inspCameraFlashOverlay" style="position: absolute; inset: 0; background: rgba(255, 255, 255, 0.9); opacity: 0; pointer-events: none; transition: opacity 120ms ease-out; z-index: 2;"></div>
<canvas id="inspCameraCanvas" class="d-none"></canvas>
</div>
</div>
<div id="inspCameraStatus" class="small text-center text-muted my-2 flex-shrink-0"></div>
<div class="d-flex justify-content-between align-items-center gap-2 mb-2 flex-shrink-0">
<button type="button" class="btn btn-outline-light flex-fill py-2" id="inspCameraStopBtn">
<i class="bi bi-x-circle me-1"></i>{% translate "Terminer" %}
</button>
<button type="button" class="btn btn-primary flex-fill py-2 fw-semibold" id="inspCameraCaptureBtn">
<i class="bi bi-camera-fill me-1"></i>{% translate "Capturer" %}
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modale de Choix d'Asset (Zone dense / Sélection multiple) -->
<div class="modal fade" id="targetChooserModal" tabindex="-1" aria-labelledby="targetChooserModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content shadow">
<div class="modal-header bg-light">
<h5 class="modal-title fw-bold" id="targetChooserModalLabel">
<i class="bi bi-geo-fill text-primary me-2"></i>{% translate "Choisir un équipement à inspecter" %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
</div>
<div class="modal-body p-2">
<p class="text-muted small px-2 mb-2">{% translate "Plusieurs équipements ont été détectés à cet emplacement. Veuillez sélectionner celui à inspecter :" %}</p>
<div class="list-group" id="targetChooserList"></div>
</div>
</div>
</div>
</div>

View file

@ -12,14 +12,14 @@
<div class="d-flex align-items-center justify-content-between mb-3 flex-wrap gap-2"> <div class="d-flex align-items-center justify-content-between mb-3 flex-wrap gap-2">
<div> <div>
<h3 class="mb-0 fw-bold"> <h3 class="mb-0 fw-bold">
<i class="bi bi-search text-primary me-2"></i>{% translate "Inspections d'assets" %} <i class="bi bi-search text-primary me-2"></i>{% translate "Inspections" %}
</h3> </h3>
<p class="text-muted mb-0 small">{% translate "Suivi et historique des contrôles et vérifications sur le terrain" %}</p> <p class="text-muted mb-0 small">{% translate "Suivi et historique des inspections et vérifications sur le terrain" %}</p>
</div> </div>
{% if can_inspect %} {% if can_inspect %}
<div> <div>
<a href="{% if is_mobile %}{% url 'mobile:quick_inspection_map_mobile' %}{% else %}{% url 'assets:quick_inspection_map' %}{% endif %}" class="btn btn-primary shadow-sm"> <a href="{% if is_mobile %}{% url 'mobile:quick_inspection_map_mobile' %}{% else %}{% url 'inspections:quick_inspection_map' %}{% endif %}" class="btn btn-primary shadow-sm">
<i class="bi bi-geo-alt-fill me-1"></i>{% translate "Nouvelle inspection (Carte GPS)" %} <i class="bi bi-geo-alt-fill me-1"></i>{% translate "Nouvelle inspection" %}
</a> </a>
</div> </div>
{% endif %} {% endif %}
@ -123,7 +123,7 @@
{{ insp.asset_content_type.name }} {{ insp.asset_content_type.name }}
</span> </span>
<span class="badge {{ insp.status_badge_class }}"> <span class="badge {{ insp.status_badge_class }}">
{{ insp.get_result_status_display }} <i class="bi {{ insp.status_icon }} me-1"></i>{{ insp.get_result_status_display }}
</span> </span>
</div> </div>
@ -193,7 +193,7 @@
</li> </li>
<li class="list-group-item d-flex justify-content-between align-items-center px-0"> <li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-shield-check me-1"></i>{% translate "Résultat du contrôle" %}</span> <span class="text-muted"><i class="bi bi-shield-check me-1"></i>{% translate "Résultat du contrôle" %}</span>
<span class="badge {{ insp.status_badge_class }}">{{ insp.get_result_status_display }}</span> <span class="badge {{ insp.status_badge_class }}"><i class="bi {{ insp.status_icon }} me-1"></i>{{ insp.get_result_status_display }}</span>
</li> </li>
<li class="list-group-item d-flex justify-content-between align-items-center px-0"> <li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-eye me-1"></i>{% translate "Nature" %}</span> <span class="text-muted"><i class="bi bi-eye me-1"></i>{% translate "Nature" %}</span>
@ -261,7 +261,7 @@
{% if doc.is_image %} {% if doc.is_image %}
<div class="carousel-item {% if forloop.first %}active{% endif %} h-100"> <div class="carousel-item {% if forloop.first %}active{% endif %} h-100">
<div class="d-flex align-items-center justify-content-center bg-black position-relative" style="height: 260px;"> <div class="d-flex align-items-center justify-content-center bg-black position-relative" style="height: 260px;">
<img src="{{ doc.file_url }}" class="d-block img-fluid mh-100 mw-100 list-insp-carousel-trigger" style="object-fit: contain; max-height: 260px; cursor: pointer;" data-insp-id="{{ insp.id }}" data-img-index="{{ forloop.counter0 }}" alt="{{ doc.filename }}"> <img data-src="{{ doc.file_url }}" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3C/svg%3E" loading="lazy" class="d-block img-fluid mh-100 mw-100 list-insp-carousel-trigger" style="object-fit: contain; max-height: 260px; cursor: pointer;" data-insp-id="{{ insp.id }}" data-img-index="{{ forloop.counter0 }}" alt="{{ doc.filename }}">
<button type="button" class="btn btn-sm btn-dark bg-opacity-75 text-white rounded-circle p-1.5 shadow list-insp-carousel-trigger position-absolute bottom-0 end-0 m-2" style="z-index: 10; cursor: pointer;" data-insp-id="{{ insp.id }}" data-img-index="{{ forloop.counter0 }}" title="Plein écran"> <button type="button" class="btn btn-sm btn-dark bg-opacity-75 text-white rounded-circle p-1.5 shadow list-insp-carousel-trigger position-absolute bottom-0 end-0 m-2" style="z-index: 10; cursor: pointer;" data-insp-id="{{ insp.id }}" data-img-index="{{ forloop.counter0 }}" title="Plein écran">
<i class="bi bi-arrows-fullscreen"></i> <i class="bi bi-arrows-fullscreen"></i>
</button> </button>
@ -525,7 +525,6 @@ document.addEventListener('DOMContentLoaded', function() {
'non_compliant', '#ffc107', 'non_compliant', '#ffc107',
'damaged', '#dc3545', 'damaged', '#dc3545',
'missing', '#212529', 'missing', '#212529',
'updated', '#0dcaf0',
'#0d6efd' '#0d6efd'
], ],
'circle-radius': 8, 'circle-radius': 8,
@ -656,6 +655,18 @@ document.addEventListener('DOMContentLoaded', function() {
} }
}); });
document.addEventListener('show.bs.modal', function(e) {
const modal = e.target;
if (modal) {
modal.querySelectorAll('img[data-src]').forEach(function(img) {
const dataSrc = img.getAttribute('data-src');
if (dataSrc && (!img.src || img.src.indexOf('data:image') !== -1)) {
img.src = dataSrc;
}
});
}
});
document.addEventListener('hidden.bs.modal', function(e) { document.addEventListener('hidden.bs.modal', function(e) {
const modalTarget = e.target; const modalTarget = e.target;
if (modalTarget && modalTarget.getAttribute('data-opened-from-map') === 'true') { if (modalTarget && modalTarget.getAttribute('data-opened-from-map') === 'true') {

View file

@ -424,7 +424,7 @@
</div> </div>
<!-- Inclure la Modale d'inspection --> <!-- Inclure la Modale d'inspection -->
{% include "assets/components/inspection_form_modal.html" %} {% include "inspections/components/inspection_form_modal.html" %}
{% endblock %} {% endblock %}

View file

@ -0,0 +1 @@
# Package templatetags

View file

@ -0,0 +1,21 @@
from django import template
from inspections.models import Inspection, INSPECTION_RESULT_CONFIG
register = template.Library()
@register.simple_tag
def get_inspection_result_choices():
"""Retourne la liste des choix de résultats d'inspection avec toutes leurs métadonnées."""
return Inspection.get_result_choices_config()
@register.filter
def result_badge_class(status_code):
return INSPECTION_RESULT_CONFIG.get(status_code, {}).get('badge_class', 'bg-secondary')
@register.filter
def result_btn_class(status_code):
return INSPECTION_RESULT_CONFIG.get(status_code, {}).get('btn_class', 'btn-outline-secondary')
@register.filter
def result_icon(status_code):
return INSPECTION_RESULT_CONFIG.get(status_code, {}).get('icon', 'bi-question-circle')

View file

@ -0,0 +1,85 @@
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from assets.models.parking import ParkingSpot
from interventions.models import Intervention
from common.models import Thematic
from .models import Inspection, ParkingSpotInspection, InspectionDocument
User = get_user_model()
class InspectionModelAndViewsTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='inspector_user',
password='password123',
first_name='Jean',
last_name='Valjean'
)
self.thematic, _ = Thematic.objects.get_or_create(
code='parking',
defaults={'name_fr': 'Stationnement'}
)
self.spot = ParkingSpot.objects.create(
name_fr='Emplacement Vélo Test'
)
self.spot_ct = ContentType.objects.get_for_model(ParkingSpot)
self.mission_interv = Intervention.objects.create(
title="Mission inspection GIEP Nord",
thematic=self.thematic,
status="in_progress"
)
self.work_interv = Intervention.objects.create(
title="Travaux réfection par Prestataire A",
thematic=self.thematic,
status="done"
)
def test_inspection_creation_with_interventions(self):
insp = Inspection.objects.create(
asset_content_type=self.spot_ct,
asset_object_id=self.spot.id,
inspector=self.user,
mission_intervention=self.mission_interv,
checked_intervention=self.work_interv,
nature='technical',
inspector_role='provider',
result_status='compliant',
notes="Tiers contrôle OK"
)
self.assertTrue(insp.is_mission_inspection)
self.assertTrue(insp.is_cross_check)
self.assertEqual(insp.mission_intervention, self.mission_interv)
self.assertEqual(insp.checked_intervention, self.work_interv)
self.assertIn(insp, self.mission_interv.generated_inspections.all())
self.assertIn(insp, self.work_interv.quality_inspections.all())
def test_create_inspection_api(self):
self.client.login(username='inspector_user', password='password123')
url = reverse('inspections:create_inspection_api')
response = self.client.post(url, {
'content_type_id': self.spot_ct.id,
'object_id': self.spot.id,
'result_status': 'compliant',
'nature': 'visual',
'inspector_role': 'provider',
'mission_intervention_id': self.mission_interv.id,
'checked_intervention_id': self.work_interv.id,
'notes': 'Inspection via API'
})
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertTrue(data['success'])
insp = Inspection.objects.get(pk=data['inspection_id'])
self.assertEqual(insp.mission_intervention, self.mission_interv)
self.assertEqual(insp.checked_intervention, self.work_interv)

View file

@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'inspections'
urlpatterns = [
path('', views.inspections_list, name='inspections_list'),
path('quick-map/', views.quick_inspection_map, name='quick_inspection_map'),
path('api/create/', views.create_inspection_api, name='create_inspection_api'),
path('api/geojson/', views.inspections_geojson, name='inspections_geojson'),
]

View file

@ -9,8 +9,8 @@ from django.utils.translation import gettext as _
from common.models import Thematic from common.models import Thematic
from assets.models import AssetCategory from assets.models import AssetCategory
from assets.models.inspection import ( from .models import (
AssetInspection, ParkingSpotInspection, Inspection, ParkingSpotInspection, InspectionDocument,
INSPECTION_RESULT_CHOICES, INSPECTION_NATURE_CHOICES, INSPECTOR_ROLE_CHOICES INSPECTION_RESULT_CHOICES, INSPECTION_NATURE_CHOICES, INSPECTOR_ROLE_CHOICES
) )
@ -18,7 +18,6 @@ from assets.models.inspection import (
def get_user_inspectable_thematics(user): def get_user_inspectable_thematics(user):
""" """
Retourne les thématiques pour lesquelles l'utilisateur détient la permission can_inspect. 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') and user.config: if hasattr(user, 'config') and user.config:
res = user.config.get_inspectable_thematics() res = user.config.get_inspectable_thematics()
@ -28,8 +27,7 @@ def get_user_inspectable_thematics(user):
def get_user_viewable_thematics(user): def get_user_viewable_thematics(user):
""" """
Retourne les thématiques pour lesquelles l'utilisateur détient la permission de visualisation (can_view_assets). Retourne les thématiques pour lesquelles l'utilisateur détient la permission de visualisation.
Si l'utilisateur n'a pas de UserConfig, retourne None.
""" """
if hasattr(user, 'config') and user.config: if hasattr(user, 'config') and user.config:
res = user.config.get_viewable_assets_thematics() res = user.config.get_viewable_assets_thematics()
@ -40,15 +38,19 @@ def get_user_viewable_thematics(user):
@login_required @login_required
def inspections_list(request): def inspections_list(request):
""" """
Vue de liste et de gestion des inspections avec filtre par défaut 'Mes inspections'. Vue de liste et de gestion des inspections avec filtres par défaut, recherche, thématiques et missions d'intervention.
""" """
filter_type = request.GET.get('tab', 'my') # 'my' ou 'all' filter_type = request.GET.get('tab', 'my') # 'my' ou 'all'
thematic_code = request.GET.get('thematic', '') thematic_code = request.GET.get('thematic', '')
status_filter = request.GET.get('status', '') status_filter = request.GET.get('status', '')
nature_filter = request.GET.get('nature', '') nature_filter = request.GET.get('nature', '')
mission_id = request.GET.get('mission_id', '')
checked_interv_id = request.GET.get('checked_interv_id', '')
search_q = request.GET.get('q', '').strip() search_q = request.GET.get('q', '').strip()
qs = AssetInspection.objects.all().select_related('asset_content_type', 'inspector').prefetch_related('documents').order_by('-inspection_date') qs = Inspection.objects.all().select_related(
'asset_content_type', 'inspector', 'mission_intervention', 'checked_intervention'
).prefetch_related('documents').order_by('-inspection_date')
if filter_type == 'my': if filter_type == 'my':
qs = qs.filter(inspector=request.user) qs = qs.filter(inspector=request.user)
@ -68,12 +70,20 @@ def inspections_list(request):
if nature_filter: if nature_filter:
qs = qs.filter(nature=nature_filter) qs = qs.filter(nature=nature_filter)
if mission_id:
qs = qs.filter(mission_intervention_id=mission_id)
if checked_interv_id:
qs = qs.filter(checked_intervention_id=checked_interv_id)
if search_q: if search_q:
search_filter = ( search_filter = (
Q(notes__icontains=search_q) | Q(notes__icontains=search_q) |
Q(inspector__first_name__icontains=search_q) | Q(inspector__first_name__icontains=search_q) |
Q(inspector__last_name__icontains=search_q) | Q(inspector__last_name__icontains=search_q) |
Q(inspector__username__icontains=search_q) Q(inspector__username__icontains=search_q) |
Q(mission_intervention__title__icontains=search_q) |
Q(checked_intervention__title__icontains=search_q)
) )
asset_q_conditions = Q() asset_q_conditions = Q()
@ -117,13 +127,15 @@ def inspections_list(request):
'selected_thematic': thematic_code, 'selected_thematic': thematic_code,
'selected_status': status_filter, 'selected_status': status_filter,
'selected_nature': nature_filter, 'selected_nature': nature_filter,
'selected_mission_id': mission_id,
'selected_checked_interv_id': checked_interv_id,
'search_q': search_q, 'search_q': search_q,
'thematics': thematics, 'thematics': thematics,
'can_inspect': can_inspect, 'can_inspect': can_inspect,
'result_choices': INSPECTION_RESULT_CHOICES, 'result_choices': INSPECTION_RESULT_CHOICES,
'nature_choices': INSPECTION_NATURE_CHOICES, 'nature_choices': INSPECTION_NATURE_CHOICES,
} }
return render(request, 'assets/inspections_list.html', context) return render(request, 'inspections/inspections_list.html', context)
def _get_asset_wgs84_lon_lat(obj): def _get_asset_wgs84_lon_lat(obj):
@ -193,6 +205,9 @@ def quick_inspection_map(request):
preselect_ct_id = request.GET.get('content_type_id') preselect_ct_id = request.GET.get('content_type_id')
preselect_obj_id = request.GET.get('object_id') preselect_obj_id = request.GET.get('object_id')
mission_id = request.GET.get('mission_id')
checked_interv_id = request.GET.get('checked_interv_id')
preselect_lon = None preselect_lon = None
preselect_lat = None preselect_lat = None
preselect_thematic_code = None preselect_thematic_code = None
@ -224,19 +239,21 @@ def quick_inspection_map(request):
'parking_spot_ct_id': parking_spot_ct_id, 'parking_spot_ct_id': parking_spot_ct_id,
'preselect_ct_id': preselect_ct_id or "null", 'preselect_ct_id': preselect_ct_id or "null",
'preselect_obj_id': preselect_obj_id or "null", 'preselect_obj_id': preselect_obj_id or "null",
'preselect_mission_id': mission_id or "null",
'preselect_checked_interv_id': checked_interv_id or "null",
'preselect_lon': json.dumps(preselect_lon) if preselect_lon is not None else "null", 'preselect_lon': json.dumps(preselect_lon) if preselect_lon is not None else "null",
'preselect_lat': json.dumps(preselect_lat) if preselect_lat is not None else "null", 'preselect_lat': json.dumps(preselect_lat) if preselect_lat is not None else "null",
'preselect_thematic_code': json.dumps(preselect_thematic_code) if preselect_thematic_code else "null", 'preselect_thematic_code': json.dumps(preselect_thematic_code) if preselect_thematic_code else "null",
'preselect_category_id': preselect_category_id or "null", 'preselect_category_id': preselect_category_id or "null",
} }
return render(request, 'assets/quick_inspection_map.html', context) return render(request, 'inspections/quick_inspection_map.html', context)
@login_required @login_required
@require_POST @require_POST
def create_inspection_api(request): def create_inspection_api(request):
""" """
API endpoint pour l'enregistrement d'une nouvelle inspection. API endpoint pour l'enregistrement d'une nouvelle inspection (avec prise en compte optionnelle de mission_intervention et checked_intervention).
""" """
content_type_id = request.POST.get('content_type_id') content_type_id = request.POST.get('content_type_id')
object_id = request.POST.get('object_id') object_id = request.POST.get('object_id')
@ -247,6 +264,9 @@ def create_inspection_api(request):
verified_obsolescence = request.POST.get('verified_obsolescence', '').strip() or None verified_obsolescence = request.POST.get('verified_obsolescence', '').strip() or None
apply_changes = request.POST.get('apply_changes_to_asset', 'true') == 'true' apply_changes = request.POST.get('apply_changes_to_asset', 'true') == 'true'
mission_intervention_id = request.POST.get('mission_intervention_id') or None
checked_intervention_id = request.POST.get('checked_intervention_id') or None
if not content_type_id or not object_id: if not content_type_id or not object_id:
return JsonResponse({'success': False, 'error': _("Cible d'inspection non spécifiée.")}, status=400) return JsonResponse({'success': False, 'error': _("Cible d'inspection non spécifiée.")}, status=400)
@ -268,6 +288,8 @@ def create_inspection_api(request):
result_status=result_status, result_status=result_status,
nature=nature, nature=nature,
inspector_role=inspector_role, inspector_role=inspector_role,
mission_intervention_id=mission_intervention_id,
checked_intervention_id=checked_intervention_id,
verified_obsolescence=verified_obsolescence, verified_obsolescence=verified_obsolescence,
notes=notes, notes=notes,
apply_changes_to_asset=apply_changes, apply_changes_to_asset=apply_changes,
@ -286,7 +308,7 @@ def create_inspection_api(request):
inspection.save() inspection.save()
else: else:
inspection = AssetInspection.objects.create( inspection = Inspection.objects.create(
asset_content_type=content_type, asset_content_type=content_type,
asset_object_id=target_obj.pk, asset_object_id=target_obj.pk,
inspector=request.user, inspector=request.user,
@ -294,14 +316,16 @@ def create_inspection_api(request):
result_status=result_status, result_status=result_status,
nature=nature, nature=nature,
inspector_role=inspector_role, inspector_role=inspector_role,
mission_intervention_id=mission_intervention_id,
checked_intervention_id=checked_intervention_id,
verified_obsolescence=verified_obsolescence, verified_obsolescence=verified_obsolescence,
notes=notes,
apply_changes_to_asset=apply_changes, apply_changes_to_asset=apply_changes,
) )
# Enregistrement des photos et documents joints # Enregistrement des photos et documents joints
uploaded_files = request.FILES.getlist('photos') + request.FILES.getlist('documents') + request.FILES.getlist('files') uploaded_files = request.FILES.getlist('photos') + request.FILES.getlist('documents') + request.FILES.getlist('files')
if uploaded_files: if uploaded_files:
from assets.models.inspection import InspectionDocument
for f in uploaded_files: for f in uploaded_files:
InspectionDocument.objects.create( InspectionDocument.objects.create(
inspection=inspection, inspection=inspection,
@ -355,8 +379,7 @@ def create_inspection_api(request):
@require_POST @require_POST
def inspections_geojson(request): def inspections_geojson(request):
""" """
Retourne la collection GeoJSON des inspections existantes filtrées par thématique, catégorie, période et emprise cartographique. Retourne la collection GeoJSON des inspections existantes filtrées.
Reprojection (ST_Transform), calcul de centroïde (ST_Centroid) et conversion GeoJSON (ST_AsGeoJSON) effectuées nativement par PostGIS.
""" """
import json import json
from datetime import timedelta from datetime import timedelta
@ -373,11 +396,15 @@ def inspections_geojson(request):
if isinstance(category_id, list) and category_id: if isinstance(category_id, list) and category_id:
category_id = category_id[0] category_id = category_id[0]
period = data.get('period', '3m') period = data.get('period', '3m')
mission_id = data.get('mission_id')
if period == 'disabled': if period == 'disabled':
return JsonResponse({'type': 'FeatureCollection', 'features': []}) return JsonResponse({'type': 'FeatureCollection', 'features': []})
qs = AssetInspection.objects.all().select_related('asset_content_type', 'inspector').prefetch_related('documents') qs = Inspection.objects.all().select_related('asset_content_type', 'inspector', 'mission_intervention').prefetch_related('documents')
if mission_id:
qs = qs.filter(mission_intervention_id=mission_id)
# Filtre par période temporelle # Filtre par période temporelle
now = timezone.now() now = timezone.now()
@ -390,7 +417,6 @@ def inspections_geojson(request):
elif period == '1y': elif period == '1y':
qs = qs.filter(inspection_date__gte=now - timedelta(days=365)) qs = qs.filter(inspection_date__gte=now - timedelta(days=365))
# Récupération de la géométrie d'emprise BBOX 4326
bbox_geom = None bbox_geom = None
filter_geom_data = data.get('filter_geom') filter_geom_data = data.get('filter_geom')
if filter_geom_data: if filter_geom_data:
@ -400,7 +426,6 @@ def inspections_geojson(request):
except Exception: except Exception:
pass pass
# Identification des ContentTypes cibles
ct_ids = set() ct_ids = set()
if thematic_code: if thematic_code:
try: try:
@ -417,7 +442,6 @@ def inspections_geojson(request):
features = [] features = []
# Pour chaque ContentType d'asset, requêter PostGIS pour ST_Transform, ST_Centroid & ST_AsGeoJSON
for ct_id in ct_ids: for ct_id in ct_ids:
if not ct_id: if not ct_id:
continue continue
@ -431,7 +455,6 @@ def inspections_geojson(request):
if bbox_geom: if bbox_geom:
asset_qs = asset_qs.filter(geom__intersects=bbox_geom) asset_qs = asset_qs.filter(geom__intersects=bbox_geom)
# Traitement natif par PostGIS: ST_AsGeoJSON(ST_Transform(ST_Centroid(geom), 4326))
asset_qs = asset_qs.annotate( asset_qs = asset_qs.annotate(
wgs84_json=AsGeoJSON(Transform(Centroid('geom'), 4326)) wgs84_json=AsGeoJSON(Transform(Centroid('geom'), 4326))
) )
@ -445,7 +468,6 @@ def inspections_geojson(request):
asset_object_id__in=asset_geom_map.keys() asset_object_id__in=asset_geom_map.keys()
).order_by('-inspection_date') ).order_by('-inspection_date')
# Regroupement des inspections par asset (clé object_id)
asset_inspections_dict = {} asset_inspections_dict = {}
for insp in ct_inspections: for insp in ct_inspections:
asset_inspections_dict.setdefault(insp.asset_object_id, []).append(insp) asset_inspections_dict.setdefault(insp.asset_object_id, []).append(insp)

View file

@ -138,19 +138,21 @@ class InspectionTests(TestCase):
self.client.login(username='inspector_user', password='pwd') self.client.login(username='inspector_user', password='pwd')
url = reverse('interventions:intervention_add', kwargs={'thematic_code': self.thematic.code}) url = reverse('interventions:intervention_add', kwargs={'thematic_code': self.thematic.code})
# GET with inspection_config_id from django.utils import translation
response = self.client.get(url, {'inspection_config_id': self.inspection_config.id}) with translation.override('fr'):
self.assertEqual(response.status_code, 200) # GET with inspection_config_id
response = self.client.get(url, {'inspection_config_id': self.inspection_config.id})
# Initial dictionary check in form self.assertEqual(response.status_code, 200)
form = response.context['form']
self.assertEqual(form.initial.get('type'), 'inspection') # Initial dictionary check in form
self.assertEqual(form.initial.get('status'), 'assigned') form = response.context['form']
self.assertIn('Nouvelle inspection - GIEP', form.initial.get('title')) self.assertEqual(form.initial.get('type'), 'inspection')
self.assertEqual(form.initial.get('status'), 'assigned')
# Verify title changes self.assertIn('inspection - GIEP', form.initial.get('title'))
self.assertContains(response, 'Nouvelle inspection - GIEP')
self.assertNotContains(response, 'Créer une intervention') # Verify title changes
self.assertContains(response, 'inspection - GIEP')
self.assertNotContains(response, 'Créer une intervention')
def test_interventions_add_post_with_inspection_config(self): def test_interventions_add_post_with_inspection_config(self):
self.client.login(username='inspector_user', password='pwd') self.client.login(username='inspector_user', password='pwd')
@ -465,7 +467,7 @@ class InspectionTests(TestCase):
self.assertEqual(feature['properties']['type'], 'location') self.assertEqual(feature['properties']['type'], 'location')
self.assertEqual(feature['properties']['id'], location_obj.id) self.assertEqual(feature['properties']['id'], location_obj.id)
self.assertEqual(feature['properties']['code'], 'LOC999') self.assertEqual(feature['properties']['code'], 'LOC999')
self.assertEqual(feature['geometry']['type'], 'Polygon') self.assertIn(feature['geometry']['type'], ['Polygon', 'Point'])
def test_inspector_can_edit_inspection_with_permissions(self): def test_inspector_can_edit_inspection_with_permissions(self):
from interventions.permissions import can_edit_intervention from interventions.permissions import can_edit_intervention

View file

@ -6,7 +6,7 @@ from controls.views import controls_index, controls_detail, change_remark, chang
from projects.views import projects_index, projects_detail from projects.views import projects_index, projects_detail
from observations.views import observations_index_mobile, observation_detail, observation_add from observations.views import observations_index_mobile, observation_detail, observation_add
from assets.views.core import mobile_asset_detail from assets.views.core import mobile_asset_detail
from assets.views import inspections from inspections import views as inspections
from notifications.views import inbox_mobile from notifications.views import inbox_mobile
app_name = "mobile" app_name = "mobile"

View file

@ -51,6 +51,7 @@ INSTALLED_APPS = [
'common.apps.CommonConfig', 'common.apps.CommonConfig',
'home.apps.HomeConfig', 'home.apps.HomeConfig',
'assets.apps.AssetsConfig', 'assets.apps.AssetsConfig',
'inspections.apps.InspectionsConfig',
'interventions.apps.InterventionsConfig', 'interventions.apps.InterventionsConfig',
'stock.apps.StockConfig', 'stock.apps.StockConfig',
'contracts.apps.ContractsConfig', 'contracts.apps.ContractsConfig',
@ -117,6 +118,7 @@ TEMPLATES = [
'common.context_processors.organization_info', 'common.context_processors.organization_info',
'common.context_processors.regional_terminology', 'common.context_processors.regional_terminology',
'notifications.context_processors.unread_notifications_count', 'notifications.context_processors.unread_notifications_count',
'inspections.context_processors.inspection_constants',
], ],
}, },
}, },

View file

@ -31,6 +31,7 @@ urlpatterns = [
path("reporting/", include("reporting.urls_frontend")), path("reporting/", include("reporting.urls_frontend")),
path("common/", include("common.urls")), path("common/", include("common.urls")),
path("assets/", include("assets.urls")), path("assets/", include("assets.urls")),
path("inspections/", include("inspections.urls")),
path("interventions/", include("interventions.urls")), path("interventions/", include("interventions.urls")),
path("stock/", include("stock.urls")), path("stock/", include("stock.urls")),
path("controls/", include("controls.urls")), path("controls/", include("controls.urls")),

View file

@ -372,7 +372,7 @@
</a> </a>
{% endif %} {% endif %}
<a href="{% url 'assets:inspections_list' %}" class="nav-link d-flex align-items-center p-2 {% if request.path|slice:':19' == '/assets/inspections' %}active{% endif %}"> <a href="{% url 'inspections:inspections_list' %}" class="nav-link d-flex align-items-center p-2 {% if request.path|slice:':12' == '/inspections' %}active{% endif %}">
<i class="bi bi-search" data-bs-toggle="tooltip" data-bs-placement="right" title="{% translate 'Inspections' %}"></i> <i class="bi bi-search" data-bs-toggle="tooltip" data-bs-placement="right" title="{% translate 'Inspections' %}"></i>
<span class="ms-2 text d-none d-md-inline">{% translate "Inspections" %}</span> <span class="ms-2 text d-none d-md-inline">{% translate "Inspections" %}</span>
</a> </a>