from django.db import models from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey from django.utils import timezone from django.utils.translation import gettext_lazy as _ INSPECTION_RESULT_CHOICES = ( ('compliant', _('Conforme')), ('non_compliant', _('Non conforme / Anomalie')), ('updated', _('Données corrigées')), ('damaged', _('Endommagé')), ('missing', _('Absent / Retiré')), ) class AssetInspection(models.Model): """ Modèle d'enregistrement générique d'une vérification/inspection sur le terrain d'un asset. Contient uniquement les données communes à tous les assets. """ asset_content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE, verbose_name=_("Type d'asset"), limit_choices_to={'app_label': 'assets'} ) asset_object_id = models.PositiveIntegerField(verbose_name=_("ID de l'asset")) asset = GenericForeignKey('asset_content_type', 'asset_object_id') inspector = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='asset_inspections', verbose_name=_("Inspecteur / Vérificateur") ) inspection_date = models.DateTimeField(default=timezone.now, verbose_name=_("Date d'inspection")) result_status = models.CharField( max_length=50, choices=INSPECTION_RESULT_CHOICES, default='compliant', verbose_name=_("Résultat du contrôle") ) verified_obsolescence = models.CharField( max_length=50, null=True, blank=True, verbose_name=_("État d'obsolescence constaté") ) notes = models.TextField(null=True, blank=True, verbose_name=_("Remarques / Constats")) apply_changes_to_asset = models.BooleanField( default=True, verbose_name=_("Appliquer directement les modifications à l'asset") ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = _("Inspection d'asset (Générique)") verbose_name_plural = _("Inspections d'assets (Génériques)") ordering = ['-inspection_date'] def save(self, *args, **kwargs): super().save(*args, **kwargs) if self.apply_changes_to_asset and self.asset: target_asset = self.asset modified = False if hasattr(target_asset, 'last_inspection_date'): target_asset.last_inspection_date = self.inspection_date modified = True if hasattr(target_asset, 'inspection_status'): target_asset.inspection_status = self.result_status modified = True if hasattr(target_asset, 'validation_status'): target_asset.validation_status = 'validated' modified = True if hasattr(target_asset, 'is_compliant'): if self.result_status in ('non_compliant', 'damaged', 'missing'): target_asset.is_compliant = False if hasattr(target_asset, 'non_compliance_reason') and self.notes: target_asset.non_compliance_reason = self.notes else: target_asset.is_compliant = True if hasattr(target_asset, 'non_compliance_reason'): target_asset.non_compliance_reason = None modified = True if self.verified_obsolescence and hasattr(target_asset, 'obsolescence'): target_asset.obsolescence = self.verified_obsolescence modified = True if modified: target_asset.save() def __str__(self): asset_str = str(self.asset) if self.asset else f"Asset #{self.asset_object_id}" 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()}" class ParkingSpotInspection(AssetInspection): """ Spécialisation de l'inspection pour les emplacements de stationnement (vélos, trottinettes, livraison, PMR). """ verified_standard_stand_count = models.IntegerField( null=True, blank=True, verbose_name=_("Nombre d'arceaux classiques constaté") ) verified_cargo_stand_count = models.IntegerField( null=True, blank=True, verbose_name=_("Nombre d'arceaux cargo constaté") ) verified_stand_type = models.CharField( max_length=50, null=True, blank=True, verbose_name=_("Type d'arceau constaté") ) verified_has_scooter_drop_zone = models.BooleanField( null=True, blank=True, verbose_name=_("Drop zone trottinette constatée") ) verified_marking_quality = models.CharField( max_length=50, null=True, blank=True, verbose_name=_("Qualité du marquage constatée") ) class Meta: verbose_name = _("Inspection d'emplacement de stationnement") verbose_name_plural = _("Inspections d'emplacements de stationnement") def save(self, *args, **kwargs): super().save(*args, **kwargs) if self.apply_changes_to_asset and self.asset and self.asset.__class__.__name__ == 'ParkingSpot': target_asset = self.asset modified = False if self.verified_standard_stand_count is not None: target_asset.standard_stand_count = self.verified_standard_stand_count modified = True if self.verified_cargo_stand_count is not None: target_asset.cargo_stand_count = self.verified_cargo_stand_count modified = True if self.verified_stand_type is not None: target_asset.stand_type = self.verified_stand_type modified = True if self.verified_has_scooter_drop_zone is not None: target_asset.has_scooter_drop_zone = self.verified_has_scooter_drop_zone modified = True if self.verified_marking_quality is not None: target_asset.marking_quality = self.verified_marking_quality modified = True if modified: target_asset.save()