feat: implement generic inspection system for assets and interventions

This commit is contained in:
Karl Determe 2026-08-10 12:28:18 +02:00
parent bc08402f34
commit 93f568a9ac
20 changed files with 297 additions and 28 deletions

View file

@ -694,7 +694,7 @@ class ParkingLocationAdmin(AssetGISAdmin):
class ParkingSpotAdmin(AssetGISAdmin): class ParkingSpotAdmin(AssetGISAdmin):
list_display = ( list_display = (
"code", "name_fr", "spot_type", "stand_type", "code", "name_fr", "spot_type", "stand_type",
"standard_stand_count", "cargo_stand_count", "total_stand_count", "standard_stand_count", "cargo_stand_count", "drop_off_stand_count", "total_stand_count",
"has_scooter_drop_zone", "is_compliant", "inspection_status", "status" "has_scooter_drop_zone", "is_compliant", "inspection_status", "status"
) )
list_filter = ( list_filter = (

View file

@ -972,7 +972,7 @@ msgstr "À inspecter"
#: assets/models/core.py:260 #: assets/models/core.py:260
#: assets/views/core.py:852 #: assets/views/core.py:852
msgid "Conforme" msgid "Conforme"
msgstr "Normal" msgstr "Conforme"
#: assets/models/core.py:261 #: assets/models/core.py:261
#: assets/views/core.py:853 #: assets/views/core.py:853
@ -2102,7 +2102,7 @@ msgstr "Autres arceaux / agencements"
#: assets/models/parking.py:90 #: assets/models/parking.py:90
msgid "Type d'arceau principal" msgid "Type d'arceau principal"
msgstr "Type d'asset inconnu" msgstr "Type d'arceau principal"
#: assets/models/parking.py:93 #: assets/models/parking.py:93
msgid "Capacité estimée trottinettes" msgid "Capacité estimée trottinettes"

View file

@ -0,0 +1,39 @@
# Generated by Django 6.0.7 on 2026-08-10 08:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0015_parkingspotinspection_apply_physical_counts_to_asset'),
]
operations = [
migrations.RemoveField(
model_name='assetinspection',
name='asset_content_type',
),
migrations.RemoveField(
model_name='assetinspection',
name='inspector',
),
migrations.RemoveField(
model_name='inspectiondocument',
name='inspection',
),
migrations.AddField(
model_name='parkingspot',
name='drop_off_stand_count',
field=models.IntegerField(default=0, verbose_name="Nombre d'arceaux drop-off"),
),
migrations.DeleteModel(
name='ParkingSpotInspection',
),
migrations.DeleteModel(
name='AssetInspection',
),
migrations.DeleteModel(
name='InspectionDocument',
),
]

View file

@ -0,0 +1,59 @@
from django.db import migrations
def create_bike_parking_category(apps, schema_editor):
Thematic = apps.get_model('common', 'Thematic')
AssetCategory = apps.get_model('assets', 'AssetCategory')
ContentType = apps.get_model('contenttypes', 'ContentType')
try:
thematic = Thematic.objects.get(code='parking')
except Thematic.DoesNotExist:
# Fallback if thematic doesn't exist yet
thematic, _ = Thematic.objects.get_or_create(
code='parking',
defaults={
'name_fr': 'Stationnement & Mobilité',
'name_nl': 'Parkeren & Mobiliteit',
'description': 'Thématique dédiée aux emplacements de stationnement et zones de mobilité (vélos, trottinettes, PMR, livraison)',
'icon': 'bi-p-square',
'color': '#0d6efd',
}
)
category, _cat_created = AssetCategory.objects.get_or_create(
code='bike_parking_spot',
defaults={
'thematic': thematic,
'name_fr': 'Emplacement vélos',
'name_nl': 'Fietsenstalling',
}
)
# Link ContentTypes
try:
spot_ct = ContentType.objects.get(app_label='assets', model='parkingspot')
category.allowed_models.add(spot_ct)
except Exception:
pass
try:
loc_ct = ContentType.objects.get(app_label='assets', model='parkinglocation')
category.allowed_models.add(loc_ct)
except Exception:
pass
def remove_bike_parking_category(apps, schema_editor):
AssetCategory = apps.get_model('assets', 'AssetCategory')
AssetCategory.objects.filter(code='bike_parking_spot').delete()
class Migration(migrations.Migration):
dependencies = [
('assets', '0016_remove_assetinspection_asset_content_type_and_more'),
]
operations = [
migrations.RunPython(create_bike_parking_category, remove_bike_parking_category),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 6.0.7 on 2026-08-10 09:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0017_add_bike_parking_category'),
]
operations = [
migrations.RemoveField(
model_name='parkingspot',
name='scooter_capacity',
),
]

View file

@ -82,6 +82,7 @@ class ParkingSpot(AbstractGeoAsset):
# Métriques et types d'arceaux vélos # Métriques et types d'arceaux vélos
standard_stand_count = models.IntegerField(default=0, verbose_name=_("Nombre d'arceaux vélos classiques")) standard_stand_count = models.IntegerField(default=0, verbose_name=_("Nombre d'arceaux vélos classiques"))
cargo_stand_count = models.IntegerField(default=0, verbose_name=_("Nombre d'arceaux vélos cargo")) cargo_stand_count = models.IntegerField(default=0, verbose_name=_("Nombre d'arceaux vélos cargo"))
drop_off_stand_count = models.IntegerField(default=0, verbose_name=_("Nombre d'arceaux drop-off"))
other_stand_count = models.IntegerField(default=0, verbose_name=_("Autres arceaux / agencements")) other_stand_count = models.IntegerField(default=0, verbose_name=_("Autres arceaux / agencements"))
stand_type = models.CharField( stand_type = models.CharField(
max_length=50, max_length=50,
@ -90,7 +91,6 @@ class ParkingSpot(AbstractGeoAsset):
verbose_name=_("Type d'arceau principal") verbose_name=_("Type d'arceau principal")
) )
has_scooter_drop_zone = models.BooleanField(default=False, verbose_name=_("Drop zone trottinettes / EDPM")) has_scooter_drop_zone = models.BooleanField(default=False, verbose_name=_("Drop zone trottinettes / EDPM"))
scooter_capacity = models.IntegerField(null=True, blank=True, verbose_name=_("Capacité estimée trottinettes"))
# Métriques générales & stationnement auto/livraison/PMR # Métriques générales & stationnement auto/livraison/PMR
capacity = models.IntegerField(null=True, blank=True, verbose_name=_("Capacité générale (nbr véhicules)")) capacity = models.IntegerField(null=True, blank=True, verbose_name=_("Capacité générale (nbr véhicules)"))
@ -124,8 +124,8 @@ class ParkingSpot(AbstractGeoAsset):
@property @property
def total_stand_count(self): def total_stand_count(self):
"""Retourne le nombre total d'arceaux (classiques + cargo + autres).""" """Retourne le nombre total d'arceaux (classiques + cargo + drop-off + autres)."""
return (self.standard_stand_count or 0) + (self.cargo_stand_count or 0) + (self.other_stand_count or 0) return (self.standard_stand_count or 0) + (self.cargo_stand_count or 0) + (self.drop_off_stand_count or 0) + (self.other_stand_count or 0)
class Meta: class Meta:
verbose_name = _("Emplacement de stationnement") verbose_name = _("Emplacement de stationnement")

View file

@ -126,6 +126,12 @@
<span class="badge bg-info text-dark rounded-pill fs-6">{{ leaf.verified_cargo_stand_count }}</span> <span class="badge bg-info text-dark rounded-pill fs-6">{{ leaf.verified_cargo_stand_count }}</span>
</li> </li>
{% endif %} {% endif %}
{% if leaf and leaf.verified_drop_off_stand_count is not None %}
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-scooter me-1 text-warning"></i>{% translate "Arceaux drop-off constatés" %}</span>
<span class="badge bg-warning text-dark rounded-pill fs-6">{{ leaf.verified_drop_off_stand_count }}</span>
</li>
{% endif %}
{% endwith %} {% endwith %}
</ul> </ul>

View file

@ -48,13 +48,13 @@ class ParkingSpotAndInspectionTest(TestCase):
cargo_stand_count=2, cargo_stand_count=2,
stand_type="mixed", stand_type="mixed",
has_scooter_drop_zone=True, has_scooter_drop_zone=True,
scooter_capacity=10,
geom=poly geom=poly
) )
def test_parking_spot_creation_and_stand_counts(self): def test_parking_spot_creation_and_stand_counts(self):
self.assertEqual(self.spot.standard_stand_count, 3) self.assertEqual(self.spot.standard_stand_count, 3)
self.assertEqual(self.spot.cargo_stand_count, 2) self.assertEqual(self.spot.cargo_stand_count, 2)
self.assertEqual(self.spot.drop_off_stand_count, 0)
self.assertEqual(self.spot.total_stand_count, 5) self.assertEqual(self.spot.total_stand_count, 5)
self.assertTrue(self.spot.has_scooter_drop_zone) self.assertTrue(self.spot.has_scooter_drop_zone)
self.assertTrue(self.spot.is_compliant) self.assertTrue(self.spot.is_compliant)
@ -91,6 +91,7 @@ class ParkingSpotAndInspectionTest(TestCase):
result_status="updated", result_status="updated",
verified_standard_stand_count=4, verified_standard_stand_count=4,
verified_cargo_stand_count=3, verified_cargo_stand_count=3,
verified_drop_off_stand_count=5,
verified_has_scooter_drop_zone=True, verified_has_scooter_drop_zone=True,
verified_marking_quality="good", verified_marking_quality="good",
apply_changes_to_asset=True, apply_changes_to_asset=True,
@ -101,7 +102,8 @@ class ParkingSpotAndInspectionTest(TestCase):
self.spot.refresh_from_db() self.spot.refresh_from_db()
self.assertEqual(self.spot.standard_stand_count, 4) # Modifié avec l'opt-in self.assertEqual(self.spot.standard_stand_count, 4) # Modifié avec l'opt-in
self.assertEqual(self.spot.cargo_stand_count, 3) # Modifié avec l'opt-in self.assertEqual(self.spot.cargo_stand_count, 3) # Modifié avec l'opt-in
self.assertEqual(self.spot.total_stand_count, 7) self.assertEqual(self.spot.drop_off_stand_count, 5) # Modifié avec l'opt-in
self.assertEqual(self.spot.total_stand_count, 12)
self.assertEqual(self.spot.validation_status, "validated") self.assertEqual(self.spot.validation_status, "validated")
self.assertEqual(self.spot.inspection_status, "updated") self.assertEqual(self.spot.inspection_status, "updated")
self.assertIsNotNone(self.spot.last_inspection_date) self.assertIsNotNone(self.spot.last_inspection_date)
@ -134,6 +136,7 @@ class ParkingSpotAndInspectionTest(TestCase):
self.assertEqual(feat["properties"]["code"], "SPOT-VELO-01") self.assertEqual(feat["properties"]["code"], "SPOT-VELO-01")
self.assertEqual(feat["properties"]["standard_stand_count"], 3) self.assertEqual(feat["properties"]["standard_stand_count"], 3)
self.assertEqual(feat["properties"]["cargo_stand_count"], 2) self.assertEqual(feat["properties"]["cargo_stand_count"], 2)
self.assertEqual(feat["properties"]["drop_off_stand_count"], 0)
self.assertEqual(feat["properties"]["total_stand_count"], 5) self.assertEqual(feat["properties"]["total_stand_count"], 5)
self.assertTrue(feat["properties"]["has_scooter_drop_zone"]) self.assertTrue(feat["properties"]["has_scooter_drop_zone"])

View file

@ -174,11 +174,11 @@ def parking_assets_geojson(request):
"spot_type_display": spot.get_spot_type_display(), "spot_type_display": spot.get_spot_type_display(),
"standard_stand_count": spot.standard_stand_count, "standard_stand_count": spot.standard_stand_count,
"cargo_stand_count": spot.cargo_stand_count, "cargo_stand_count": spot.cargo_stand_count,
"drop_off_stand_count": spot.drop_off_stand_count,
"total_stand_count": spot.total_stand_count, "total_stand_count": spot.total_stand_count,
"stand_type": spot.stand_type, "stand_type": spot.stand_type,
"stand_type_display": spot.get_stand_type_display(), "stand_type_display": spot.get_stand_type_display(),
"has_scooter_drop_zone": spot.has_scooter_drop_zone, "has_scooter_drop_zone": spot.has_scooter_drop_zone,
"scooter_capacity": spot.scooter_capacity,
"capacity": spot.capacity, "capacity": spot.capacity,
"marking_quality": spot.marking_quality, "marking_quality": spot.marking_quality,
"is_compliant": spot.is_compliant, "is_compliant": spot.is_compliant,

View file

@ -32,7 +32,7 @@ class ParkingSpotInspectionAdmin(admin.ModelAdmin):
list_display = ( list_display = (
"id", "asset", "inspector", "inspection_date", "id", "asset", "inspector", "inspection_date",
"result_status", "verified_standard_stand_count", "result_status", "verified_standard_stand_count",
"verified_cargo_stand_count", "verified_has_scooter_drop_zone", "verified_cargo_stand_count", "verified_drop_off_stand_count", "verified_has_scooter_drop_zone",
"apply_changes_to_asset" "apply_changes_to_asset"
) )
list_filter = ("result_status", "apply_changes_to_asset", "inspection_date") list_filter = ("result_status", "apply_changes_to_asset", "inspection_date")

View file

@ -0,0 +1,23 @@
# Generated by Django 6.0.7 on 2026-08-10 08:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inspections', '0002_migrate_existing_inspections'),
]
operations = [
migrations.AddField(
model_name='parkingspotinspection',
name='verified_drop_off_stand_count',
field=models.IntegerField(blank=True, null=True, verbose_name="Nombre d'arceaux drop-off constaté"),
),
migrations.AlterField(
model_name='inspection',
name='result_status',
field=models.CharField(choices=[('compliant', 'Conforme'), ('non_compliant', 'Non conforme / Anomalie'), ('damaged', 'Endommagé'), ('missing', 'Absent / Retiré')], default='compliant', max_length=50, verbose_name='Résultat du contrôle'),
),
]

View file

@ -17,7 +17,7 @@ INSPECTION_RESULT_CONFIG = {
'color_hex': '#198754', 'color_hex': '#198754',
}, },
'non_compliant': { 'non_compliant': {
'label': _('Non conforme / Anomalie'), 'label': _('Non conforme'),
'badge_class': 'bg-warning text-dark', 'badge_class': 'bg-warning text-dark',
'btn_class': 'btn-outline-warning text-dark', 'btn_class': 'btn-outline-warning text-dark',
'icon': 'bi-exclamation-triangle-fill', 'icon': 'bi-exclamation-triangle-fill',
@ -331,6 +331,11 @@ class ParkingSpotInspection(Inspection):
blank=True, blank=True,
verbose_name=_("Nombre d'arceaux cargo constaté") verbose_name=_("Nombre d'arceaux cargo constaté")
) )
verified_drop_off_stand_count = models.IntegerField(
null=True,
blank=True,
verbose_name=_("Nombre d'arceaux drop-off constaté")
)
verified_stand_type = models.CharField( verified_stand_type = models.CharField(
max_length=50, max_length=50,
null=True, null=True,
@ -375,6 +380,9 @@ class ParkingSpotInspection(Inspection):
if self.verified_cargo_stand_count is not None and hasattr(target_asset, 'cargo_stand_count'): if self.verified_cargo_stand_count is not None and hasattr(target_asset, 'cargo_stand_count'):
target_asset.cargo_stand_count = self.verified_cargo_stand_count target_asset.cargo_stand_count = self.verified_cargo_stand_count
modified = True modified = True
if self.verified_drop_off_stand_count is not None and hasattr(target_asset, 'drop_off_stand_count'):
target_asset.drop_off_stand_count = self.verified_drop_off_stand_count
modified = True
if self.verified_has_scooter_drop_zone is not None and hasattr(target_asset, 'has_scooter_drop_zone'): if self.verified_has_scooter_drop_zone is not None and hasattr(target_asset, 'has_scooter_drop_zone'):
target_asset.has_scooter_drop_zone = self.verified_has_scooter_drop_zone target_asset.has_scooter_drop_zone = self.verified_has_scooter_drop_zone
modified = True modified = True

View file

@ -5,24 +5,87 @@
<span class="small fw-bold text-secondary mb-2 d-block"> <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" %} <i class="bi bi-p-square me-1"></i>{% translate "Contrôles spécifiques stationnement" %}
</span> </span>
<div class="row g-2 mb-2"> <div class="row g-2 mb-3">
<div class="col-6"> <div class="col-4">
<label class="form-label small mb-1">{% translate "Arceaux classiques" %}</label> <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" placeholder="{% translate 'Saisir le nombre constaté' %}"> <input type="number" name="verified_standard_stand_count" id="insp_std_stands"
class="form-control form-control-sm" min="0">
</div>
<div class="col-4">
<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 class="col-4">
<label class="form-label small mb-1">{% translate "Arceaux drop-off" %}</label>
<input type="number" name="verified_drop_off_stand_count" id="insp_drop_off_stands"
class="form-control form-control-sm" min="0">
</div>
</div>
<!-- Drop zone trottinettes -->
<div class="mb-3">
<label class="form-label small fw-bold text-muted mb-1">{% translate "Drop zone trottinettes" %}</label>
<div class="row g-2">
<div class="col-6">
<input type="radio" class="btn-check" name="verified_has_scooter_drop_zone" id="dz_yes" value="true">
<label class="btn btn-outline-secondary btn-sm w-100"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="dz_yes">
<i class="bi bi-check-circle me-1"></i>{% translate "Oui" %}
</label>
</div> </div>
<div class="col-6"> <div class="col-6">
<label class="form-label small mb-1">{% translate "Arceaux cargo" %}</label> <input type="radio" class="btn-check" name="verified_has_scooter_drop_zone" id="dz_no" value="false">
<input type="number" name="verified_cargo_stand_count" id="insp_cargo_stands" class="form-control form-control-sm" min="0" placeholder="{% translate 'Saisir le nombre constaté' %}"> <label class="btn btn-outline-secondary btn-sm w-100"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="dz_no">
<i class="bi bi-x-circle me-1"></i>{% translate "Non" %}
</label>
</div> </div>
</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>
<!-- Qualité marquage -->
<div class="mb-2">
<label class="form-label small fw-bold text-muted mb-1">{% translate "Qualité du marquage au sol" %}</label>
<div class="row g-1">
<div class="col-6">
<input type="radio" class="btn-check" name="verified_marking_quality" id="mq_good" value="good">
<label class="btn btn-outline-success btn-sm w-100 text-truncate"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="mq_good">
<i class="bi bi-emoji-smile me-1"></i>{% translate "Bon état" %}
</label>
</div>
<div class="col-6">
<input type="radio" class="btn-check" name="verified_marking_quality" id="mq_medium" value="medium">
<label class="btn btn-outline-warning btn-sm w-100 text-truncate"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="mq_medium">
<i class="bi bi-emoji-neutral me-1"></i>{% translate "Moyen" %}
</label>
</div>
<div class="col-6 mt-2">
<input type="radio" class="btn-check" name="verified_marking_quality" id="mq_damaged" value="damaged">
<label class="btn btn-outline-danger btn-sm w-100 text-truncate"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="mq_damaged">
<i class="bi bi-emoji-frown me-1"></i>{% translate "Dégradé" %}
</label>
</div>
<div class="col-6 mt-2">
<input type="radio" class="btn-check" name="verified_marking_quality" id="mq_missing" value="missing">
<label class="btn btn-outline-dark btn-sm w-100 text-truncate"
style="padding: 0.5rem 0.2rem; border-radius: 10px;" for="mq_missing">
<i class="bi bi-x-circle me-1"></i>{% translate "Absent" %}
</label>
</div>
</div>
</div>
{% if show_apply_physical_counts %}
<div class="form-check form-switch mt-2 bg-light p-2 rounded ps-4">
<input class="form-check-input ms-0 me-2" type="checkbox" name="apply_physical_counts_to_asset" value="true"
id="insp_apply_physical_counts" checked>
<label class="form-check-label small fw-semibold" for="insp_apply_physical_counts">
{% translate "Mettre à jour la capacité / les comptages de l'emplacement vélos" %}
</label>
</div>
{% endif %}
</div> </div>

View file

@ -87,6 +87,7 @@
{% endif %} {% endif %}
</div> </div>
{% if show_create_intervention %}
<!-- Option Création automatique d'une intervention --> <!-- Option Création automatique d'une intervention -->
<div class="form-check form-switch mb-3 bg-light p-2 rounded ps-4" id="createInterventionSwitchContainer"> <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"> <input class="form-check-input ms-0 me-2" type="checkbox" name="create_intervention" value="true" id="createInterventionSwitch">
@ -94,6 +95,7 @@
{% translate "Créer automatiquement une intervention en cas d'anomalie / dégât" %} {% translate "Créer automatiquement une intervention en cas d'anomalie / dégât" %}
</label> </label>
</div> </div>
{% endif %}
<!-- Remarques --> <!-- Remarques -->
<div class="mb-3"> <div class="mb-3">

View file

@ -217,6 +217,12 @@
<span class="badge bg-info text-dark rounded-pill fs-6">{{ leaf.verified_cargo_stand_count }}</span> <span class="badge bg-info text-dark rounded-pill fs-6">{{ leaf.verified_cargo_stand_count }}</span>
</li> </li>
{% endif %} {% endif %}
{% if leaf.verified_drop_off_stand_count is not None %}
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
<span class="text-muted"><i class="bi bi-scooter me-1 text-warning"></i>{% translate "Arceaux drop-off constatés" %}</span>
<span class="badge bg-warning text-dark rounded-pill fs-6">{{ leaf.verified_drop_off_stand_count }}</span>
</li>
{% endif %}
{% if leaf.verified_marking_quality %} {% if leaf.verified_marking_quality %}
<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-paint-bucket me-1"></i>{% translate "Qualité du marquage" %}</span> <span class="text-muted"><i class="bi bi-paint-bucket me-1"></i>{% translate "Qualité du marquage" %}</span>

View file

@ -299,13 +299,23 @@ def create_inspection_api(request):
inspection.verified_standard_stand_count = int(request.POST['verified_standard_stand_count']) inspection.verified_standard_stand_count = int(request.POST['verified_standard_stand_count'])
if 'verified_cargo_stand_count' in request.POST and request.POST['verified_cargo_stand_count'] != '': if 'verified_cargo_stand_count' in request.POST and request.POST['verified_cargo_stand_count'] != '':
inspection.verified_cargo_stand_count = int(request.POST['verified_cargo_stand_count']) inspection.verified_cargo_stand_count = int(request.POST['verified_cargo_stand_count'])
if 'verified_drop_off_stand_count' in request.POST and request.POST['verified_drop_off_stand_count'] != '':
inspection.verified_drop_off_stand_count = int(request.POST['verified_drop_off_stand_count'])
if 'verified_stand_type' in request.POST and request.POST['verified_stand_type'] != '': if 'verified_stand_type' in request.POST and request.POST['verified_stand_type'] != '':
inspection.verified_stand_type = request.POST['verified_stand_type'] inspection.verified_stand_type = request.POST['verified_stand_type']
if 'verified_has_scooter_drop_zone' in request.POST: if 'verified_has_scooter_drop_zone' in request.POST:
inspection.verified_has_scooter_drop_zone = (request.POST['verified_has_scooter_drop_zone'] == 'true') val = request.POST['verified_has_scooter_drop_zone']
if val == 'true':
inspection.verified_has_scooter_drop_zone = True
elif val == 'false':
inspection.verified_has_scooter_drop_zone = False
elif val == '':
inspection.verified_has_scooter_drop_zone = None
if 'verified_marking_quality' in request.POST and request.POST['verified_marking_quality'] != '': if 'verified_marking_quality' in request.POST and request.POST['verified_marking_quality'] != '':
inspection.verified_marking_quality = request.POST['verified_marking_quality'] inspection.verified_marking_quality = request.POST['verified_marking_quality']
inspection.apply_physical_counts_to_asset = (request.POST.get('apply_physical_counts_to_asset') == 'true')
inspection.save() inspection.save()
else: else:
inspection = Inspection.objects.create( inspection = Inspection.objects.create(

View file

@ -4138,7 +4138,7 @@ msgstr "Quel est l'état de la végétation ?"
#: interventions/templates/interventions/guided_operations/inspection_rain_water.html:2665 #: interventions/templates/interventions/guided_operations/inspection_rain_water.html:2665
#: interventions/templates/interventions/guided_operations/inspection_rain_water.html:2843 #: interventions/templates/interventions/guided_operations/inspection_rain_water.html:2843
msgid "Bon" msgid "Bon"
msgstr "Non" msgstr "Bon"
#: interventions/templates/interventions/guided_operations/inspection_rain_water.html:488 #: interventions/templates/interventions/guided_operations/inspection_rain_water.html:488
#: interventions/templates/interventions/guided_operations/inspection_rain_water.html:1183 #: interventions/templates/interventions/guided_operations/inspection_rain_water.html:1183

View file

@ -0,0 +1,33 @@
# Generated by Django 6.0.7 on 2026-08-10 08:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sign', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='signpanel',
name='funding_program',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Programme de financement / Projet'),
),
migrations.AddField(
model_name='signpanel',
name='inspection_status',
field=models.CharField(blank=True, choices=[('to_inspect', 'À inspecter'), ('compliant', 'Conforme'), ('non_compliant', 'Non conforme'), ('updated', 'Mis à jour'), ('unknown', 'Non inspecté')], default='unknown', max_length=50, null=True, verbose_name="Statut d'inspection"),
),
migrations.AddField(
model_name='signpole',
name='funding_program',
field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Programme de financement / Projet'),
),
migrations.AddField(
model_name='signpole',
name='inspection_status',
field=models.CharField(blank=True, choices=[('to_inspect', 'À inspecter'), ('compliant', 'Conforme'), ('non_compliant', 'Non conforme'), ('updated', 'Mis à jour'), ('unknown', 'Non inspecté')], default='unknown', max_length=50, null=True, verbose_name="Statut d'inspection"),
),
]