# ---------------------- # Control Centers Models # ---------------------- from django.contrib.gis.db import models from django.urls import reverse from django.utils.translation import get_language from django.core.exceptions import ValidationError from django.utils.text import slugify from assets.models import AbstractLocation, AbstractAsset, AbstractGeoAsset, AbstractAssetModel from common.models import Document from common.storages import PrivateMediaStorage import os private_storage = PrivateMediaStorage() # Control Center Location class ControlCenterLocation(AbstractLocation): geom = models.MultiPolygonField(srid=3812, null=True,blank=True) parent_location = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name="child_locations") is_main_location = models.BooleanField(default=False, verbose_name="Main Location") control_center = models.ForeignKey('ControlCenter', on_delete=models.SET_NULL, null=True, blank=True, related_name="all_locations") def get_active_assets(self): def collect_assets(location): assets = list(location.assets.filter(status='active')) for child in location.child_locations.all(): assets += collect_assets(child) return assets return collect_assets(self) def get_all_assets(self): def collect_assets(location): assets = list(location.assets.all()) for child in location.child_locations.all(): assets += collect_assets(child) return assets return collect_assets(self) def get_direct_child_assets(self): return self.assets.all() def clean(self): if self.is_main_location and ControlCenterLocation.objects.filter(control_center=self.control_center, is_main_location=True).exclude(pk=self.pk).exists(): raise ValidationError("Ce centre de contrôle a déjà une localisation principale.") def get_absolute_url(self): return reverse( 'assets:controlcenters_locations_detail', kwargs={ # 'asset_model': content_type.model, 'location_id': self.id } ) class ControlCenter(models.Model): code = models.CharField(max_length=50, unique=True) name_fr = models.CharField(max_length=200,null=True,blank=True) name_nl = models.CharField(max_length=200,null=True,blank=True) main_location = models.OneToOneField(ControlCenterLocation, on_delete=models.SET_NULL, null=True, blank=True, related_name="main_control_center") description = models.TextField(null=True, blank=True) def get_name(self): lang = get_language() name = getattr(self, f"name_{lang}", self.name_fr) or "" # Fallback to name_fr return name def get_active_assets(self): return self.assets.filter(status='active') def get_all_assets(self): return self.assets.all() def get_all_locations(self): return self.all_locations.all() def get_main_location(self): return self.main_location def __str__(self): return self.code + " - " + self.get_name() class Meta: ordering = ["code"] class ControlCenterAssetModel(AbstractAssetModel): class Meta: verbose_name = "Control Center Asset Model" verbose_name_plural = "Control Center Asset Models" class ControlCenterGeoAsset(AbstractGeoAsset): model = models.ForeignKey(ControlCenterAssetModel, on_delete=models.PROTECT, null=True, blank=True, related_name="controlcenters_assets") location = models.ForeignKey(ControlCenterLocation, related_name="assets", on_delete=models.SET_NULL, null=True, blank=True) control_center = models.ForeignKey(ControlCenter, on_delete=models.SET_NULL, related_name="assets", null=True, blank=True) class Meta: indexes = [ models.Index(fields=["code"]), ] def get_absolute_url(self): return reverse('assets:controlcenters_assets_detail', kwargs={'asset_model': 'controlcentergeoasset', 'asset_id':self.id}) class ControlCenterContract(models.Model): control_center = models.ForeignKey(ControlCenter, on_delete=models.CASCADE, related_name="controlcenters_contracts") contract = models.ForeignKey("contracts.Contract", on_delete=models.CASCADE, related_name="controlcenters_contracts") status = models.CharField(max_length=50, choices=[('active', 'Actif'), ('inactive', 'Inactif'), ('archived', 'Archivé')], default='active') maintenance_type = models.CharField(max_length=50, choices=[('building', 'Bâtiment'), ('office', 'Bureautique'), ('system', 'Système'), ('other', 'Autre')], default='other') def __str__(self): return f"{self.control_center.code} - {self.contract.contract_number}" class Meta: verbose_name = "Control Center Contract" verbose_name_plural = "Control Center Contracts" unique_together = ('control_center', 'contract') class ControlCenterGeoAssetContract(models.Model): control_center_asset = models.ForeignKey(ControlCenterGeoAsset, on_delete=models.CASCADE, related_name="controlcenters_assets_contracts") contract = models.ForeignKey("contracts.Contract", on_delete=models.CASCADE, related_name="controlcenters_assets_contracts") status = models.CharField(max_length=50, choices=[('active', 'Actif'), ('inactive', 'Inactif'), ('archived', 'Archivé')], default='active') maintenance_type = models.CharField(max_length=50, choices=[('building', 'Bâtiment'), ('office', 'Bureautique'), ('system', 'Système'), ('other', 'Autre')], default='other') def __str__(self): return f"{self.control_center_asset.code} - {self.contract.contract_number}" class Meta: verbose_name = "Control Center Asset Contract" verbose_name_plural = "Control Center Asset Contracts" unique_together = ('control_center_asset', 'contract') def upload_to_controlcenter(instance, filename): code = slugify(instance.control_center.code or "unknown") # Fallback si code est None return os.path.join("assets_documents", "controlcenters", code, filename) TU_DOCUMENT_TYPE_CHOICES = ( ('other', 'Autre'), ) # Control Center Location Document class ControlCenterDocument(Document): control_center = models.ForeignKey(ControlCenter, on_delete=models.CASCADE, related_name="documents", verbose_name="Associated Location") file = models.FileField(upload_to=upload_to_controlcenter, storage=private_storage, verbose_name="Fichier", max_length=255) thumbnail = models.ImageField(upload_to=upload_to_controlcenter, storage=private_storage, null=True, blank=True, max_length=255) document_type = models.CharField(max_length=50, choices=TU_DOCUMENT_TYPE_CHOICES, default='other', verbose_name="Type de document") # Control Center Asset Document class ControlCenterAssetDocument(Document): control_center_asset = models.ForeignKey(ControlCenterGeoAsset, on_delete=models.CASCADE, related_name="documents", verbose_name="Associated Location") file = models.FileField(upload_to=upload_to_controlcenter, storage=private_storage, verbose_name="Fichier", max_length=255) thumbnail = models.ImageField(upload_to=upload_to_controlcenter, storage=private_storage, null=True, blank=True, max_length=255) document_type = models.CharField(max_length=50, choices=TU_DOCUMENT_TYPE_CHOICES, default='other', verbose_name="Type de document")