from django.db import models from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import Group from event.models import Activity, EventSpecificMixin from django.db.models import Q from .fields import IdField from datetime import date class EquipmentCategory(models.Model): name = models.CharField( _("nom"), max_length=200, ) parent = models.ForeignKey( 'self', blank=True, null=True, default=None, on_delete=models.SET_NULL, related_name="children", help_text=_("merci de ne pas faire de référence cyclique"), verbose_name=_("parent"), ) def has_parent(self, cat): current = self for k in range(100): if current is None: return False if current == cat: return True current = current.parent def full_name(self): current = self res = "" for k in range(100): res = "/{current}{old}".format( current=current.name, old=res) if current.parent is None: break current = current.parent return res full_name.short_description = _("Chemin complet") full_name_p = property(full_name) class Meta: verbose_name = _("catégories") verbose_name_plural = _("catégories") def __str__(self): return self.name def save(self, *args, **kwargs): if self.pk: done = False current = self while not done: if current.parent == self: self.parent = None done = True elif current.parent is None: done = True current = current.parent return super().save(*args, **kwargs) class EquipmentQuerySet(models.QuerySet): def in_category(self, cat): filtre = Q(id__lt=0) childs_id = [c.id for c in EquipmentCategory.objects.all() if c.has_parent(cat)] for pk in childs_id: filtre |= Q(category__id=pk) return self.filter(filtre) class Equipment(EventSpecificMixin, models.Model): name = models.CharField( _("nom du matériel"), max_length=200, ) stock = models.PositiveSmallIntegerField(_("quantité totale")) description = models.TextField( _("description"), blank=True, ) activities = models.ManyToManyField( Activity, related_name="equipment", through="EquipmentAttribution", ) owner = models.ForeignKey( Group, verbose_name=_("propriétaire"), blank=True, null=True, on_delete=models.SET_NULL, ) category = models.ForeignKey( EquipmentCategory, verbose_name=_("catégorie"), on_delete=models.PROTECT, ) added_at = models.DateTimeField( _("ajouté le"), auto_now_add=True, ) modified_at = models.DateTimeField( _("dernière modification"), auto_now=True, ) objects = EquipmentQuerySet.as_manager() def is_in_category(self, cat): current = self.category for k in range(100): if current is None: return False if current == cat: return True current = current.parent def ids_aviable(self): if self.stock is None: return [] res = list(map(lambda x: x+1, range(self.stock))) for lost in self.losts.all(): res = [x for x in res if x not in lost.ids] # TODO cassé # TODO utilisés return res def ids_lost(self): res = [] for lost in self.losts.all(): res = res + [x for x in lost.ids if x not in res] return res def stock_aviable(self): aviable = self.ids_aviable() return len(aviable) def stock_lost(self): return len(self.ids_lost()) def full_category(self): return self.category.full_name() full_category.short_description = _("Chemin complet") ids_aviable.short_description = _("disponibles") ids_lost.short_description = _("perdus") stock_aviable.short_description = _("quantité disponible") stock_lost.short_description = _("quantité perdue") full_category_p = property(full_category) ids_aviable_p = property(ids_aviable) ids_lost_p = property(ids_lost) stock_aviable_p = property(stock_aviable) stock_lost_p = property(stock_lost) class Meta: verbose_name = _("matériel") verbose_name_plural = _("matériels") def __str__(self): return self.name class EquipmentAttribute(models.Model): name = models.CharField( _("nom"), max_length=200, unique=True, ) class Meta: verbose_name = _("attribut") verbose_name_plural = _("attributs") def __str__(self): return self.name class EquipmentAttributeValue(models.Model): equipment = models.ForeignKey( Equipment, on_delete=models.CASCADE, related_name="attributes", verbose_name=_("matériel"), help_text=_("Matériel concerné par le defaut"), ) attribute = models.ForeignKey( EquipmentAttribute, verbose_name=_("attribut"), on_delete=models.CASCADE, ) value = models.CharField( _("valeur"), max_length=200, ) class Meta: verbose_name = _("attribut de matériel") verbose_name_plural = _("attributs de matériel") def __str__(self): return "{attr}={value}".format(attr=self.attribute.name, value=self.value) class EquipmentAttribution(models.Model): equipment = models.ForeignKey( Equipment, verbose_name=_("matériel"), on_delete=models.CASCADE, ) activity = models.ForeignKey( Activity, on_delete=models.CASCADE, ) amount = models.BigIntegerField(_("quantité attribuée")) remarks = models.TextField( _("remarques concernant l'attribution"), blank=True, ) class Meta: verbose_name = _("attribution de matériel") verbose_name_plural = _("attributions de matériel") def __str__(self): return "%s (%d) -> %s" % (self.equipment.name, self.amount, self.activity.get_herited('title')) def save(self, *args, **kwargs): if (self.equipment.event and self.equipment.event != self.activity.event): raise ValidationError super(EquipmentAttribution, self).save(*args, **kwargs) class EquipmentDefault(models.Model): remark = models.TextField(_("remarque sur le défaut")) equipment = models.ForeignKey( Equipment, verbose_name=_("matériel"), on_delete=models.CASCADE, related_name="remarks", help_text=_("Matériel concerné par le defaut"), ) ids = IdField() is_unusable = models.BooleanField(_("inutilisable")) send_repare = models.BooleanField(_("à envoyer réparareur")) class Meta: verbose_name = _("defaut matériel") verbose_name_plural = _("défauts sur le matériel") def __str__(self): return "%s : %s" % (self.equipment.name, self.remark) class EquipmentLost(models.Model): lost_at = models.DateField( _("perdu le"), default=date.today, ) equipment = models.ForeignKey( Equipment, verbose_name=_("matériel"), on_delete=models.CASCADE, related_name="losts", help_text=_("Matériel concerné par la perte"), ) ids = IdField() class EquipmentRevision(models.Model): date = models.DateField( _("date"), default=date.today, ) equipment = models.ForeignKey( Equipment, verbose_name=_("matériel"), on_delete=models.CASCADE, related_name="revisions", help_text=_("Matériel concerné par les révisions"), ) remark = models.TextField(_("remarque sur la révision")) ids = IdField() class Meta: verbose_name = _("révision de matériel") verbose_name_plural = _("révisions de matériel") def __str__(self): return "%s : %s" % (self.equipment.name, self.remark)