models.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from django.db import models
  4. from django.db.models import Q
  5. from django.conf import settings
  6. from django.utils import timezone
  7. from .fields import MACAddressField
  8. class ItemType(models.Model):
  9. name = models.CharField(max_length=100, verbose_name="nom")
  10. def __unicode__(self):
  11. return self.name
  12. class Meta:
  13. verbose_name = "type d’objet"
  14. verbose_name_plural = "types d’objet"
  15. class ItemQuerySet(models.QuerySet):
  16. def _get_borrowed_pks(self):
  17. return Loan.objects.running().values_list("item", flat=True)
  18. def available(self):
  19. return self.exclude(pk__in=self._get_borrowed_pks()).exclude(deployed=True)
  20. def borrowed(self):
  21. return self.filter(pk__in=self._get_borrowed_pks())
  22. def deployed(self):
  23. return self.filter(deployed=True)
  24. def unavailable(self):
  25. """ deployed or borrowed
  26. """
  27. return self.filter(Q(pk__in=self._get_borrowed_pks()) | Q(deployed=True))
  28. class Item(models.Model):
  29. type = models.ForeignKey(
  30. ItemType, verbose_name="type de matériel", related_name="items"
  31. )
  32. designation = models.CharField(max_length=100, verbose_name="désignation")
  33. storage = models.ForeignKey(
  34. "Storage",
  35. related_name="items",
  36. verbose_name="Lieu de stockage",
  37. null=True,
  38. blank=True,
  39. help_text="Laisser vide si inconnu",
  40. )
  41. mac_address = MACAddressField(
  42. verbose_name="adresse MAC",
  43. blank=True,
  44. null=True,
  45. unique=True,
  46. help_text="préférable au n° de série si possible",
  47. )
  48. serial = models.CharField(
  49. verbose_name="N° de série",
  50. max_length=250,
  51. blank=True,
  52. null=True,
  53. unique=True,
  54. help_text="ou toute autre référence unique",
  55. )
  56. buy_date = models.DateField(verbose_name="date d’achat", blank=True, null=True)
  57. owner = models.ForeignKey(
  58. settings.AUTH_USER_MODEL,
  59. verbose_name="Propriétaire",
  60. related_name="items",
  61. null=True,
  62. blank=True,
  63. help_text="dans le cas de matériel n'appartenant pas à l'association",
  64. )
  65. deployed = models.BooleanField(
  66. verbose_name="déployé",
  67. default=False,
  68. help_text="Cocher si le matériel est en production",
  69. )
  70. comment = models.TextField(verbose_name="commentaire", blank=True, null=True)
  71. objects = ItemQuerySet().as_manager()
  72. def __unicode__(self):
  73. return self.designation
  74. def save(self, *args, **kwargs):
  75. # workaround for unique=True, null=True
  76. # see https://code.djangoproject.com/ticket/4136#comment:33
  77. self.mac_address = self.mac_address or None
  78. self.serial = self.serial or None
  79. return super(Item, self).save(*args, **kwargs)
  80. def get_current_loan(self):
  81. """
  82. Returns the current Loan for this Item, if exists, or None.
  83. """
  84. try:
  85. return self.loans.get(loan_date_end__isnull=True)
  86. except Loan.DoesNotExist:
  87. return None
  88. def is_available(self):
  89. """
  90. Returns the status of the Item. If a running loan exists,
  91. or if the item is deployed, returns False (else True).
  92. """
  93. return (not self.deployed) and (not self.loans.running().exists())
  94. is_available.boolean = True
  95. is_available.short_description = "disponible"
  96. def get_mac_and_serial(self):
  97. mac = self.mac_address
  98. serial = self.serial
  99. if mac and serial:
  100. return "{} / {}".format(mac, serial)
  101. else:
  102. return mac or serial or ""
  103. class Meta:
  104. verbose_name = "objet"
  105. ordering = ["designation", "mac_address", "serial"]
  106. def give_back(self, storage=None):
  107. self.storage = storage
  108. self.save()
  109. self.loans.running().update(loan_date_end=timezone.now())
  110. class LoanQuerySet(models.QuerySet):
  111. @staticmethod
  112. def _running_filter():
  113. return models.Q(loan_date_end__gt=timezone.now()) | models.Q(
  114. loan_date_end__isnull=True
  115. )
  116. def running(self):
  117. return self.filter(self._running_filter())
  118. def finished(self):
  119. return self.exclude(self._running_filter())
  120. class Loan(models.Model):
  121. item = models.ForeignKey(Item, verbose_name="objet", related_name="loans")
  122. user = models.ForeignKey(
  123. settings.AUTH_USER_MODEL, verbose_name="membre", related_name="loans"
  124. )
  125. loan_date = models.DateTimeField(verbose_name="date de prêt")
  126. loan_date_end = models.DateTimeField(
  127. verbose_name="date de fin de prêt", null=True, blank=True
  128. )
  129. notes = models.TextField(null=True, blank=True)
  130. def __unicode__(self):
  131. return "prêt de {item} à {user}".format(item=self.item, user=self.user)
  132. def get_mac_and_serial(self):
  133. return self.item.get_mac_and_serial()
  134. get_mac_and_serial.short_description = "Adresse MAC / n° de série"
  135. def user_can_close(self, user):
  136. return (not self.item.is_available()) and (self.user == user)
  137. def is_running(self):
  138. return not self.loan_date_end or self.loan_date_end > timezone.now()
  139. is_running.boolean = True
  140. is_running.short_description = "En cours ?"
  141. class Meta:
  142. verbose_name = "prêt d’objet"
  143. verbose_name_plural = "prêts d’objets"
  144. objects = LoanQuerySet().as_manager()
  145. class Storage(models.Model):
  146. name = models.CharField(max_length=100, verbose_name="nom")
  147. notes = models.TextField(blank=True, help_text="Lisible par tous les adhérents")
  148. def __unicode__(self):
  149. return self.name
  150. def items_count(self):
  151. return self.items.count()
  152. items_count.short_description = "Nb. items stockés"
  153. class Meta:
  154. verbose_name = "lieu de stockage"
  155. verbose_name_plural = "lieux de stockage"