models.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. from django.conf import settings
  2. from django.core.exceptions import ValidationError
  3. from django.core.urlresolvers import reverse
  4. from django.db import models
  5. from .validators import less_than_one
  6. class AbstractItem(models.Model):
  7. name = models.CharField(max_length=130)
  8. description = models.TextField(blank=True)
  9. def __str__(self):
  10. return self.name
  11. def get_use_class(self):
  12. raise NotImplemented
  13. def used(self, except_by=None):
  14. """ Return the used fraction of an item
  15. :type: Service
  16. :param except_by: exclude this service from the math
  17. :rtype: float
  18. """
  19. sharing_costs = self.get_use_class().objects.filter(resource=self)
  20. if except_by:
  21. sharing_costs = sharing_costs.exclude(service=except_by)
  22. existing_uses_sum = sum(
  23. sharing_costs.values_list('share', flat=True))
  24. return existing_uses_sum
  25. class Meta:
  26. abstract = True
  27. class Cost(AbstractItem):
  28. """ A monthtly cost we have to pay
  29. """
  30. price = models.FloatField(help_text="Coût mensuel")
  31. def get_use_class(self):
  32. return CostUse
  33. class Meta:
  34. verbose_name = 'Coût'
  35. class Good(AbstractItem):
  36. """ A good, which replacement is provisioned
  37. """
  38. price = models.FloatField()
  39. provisioning_duration = models.DurationField(
  40. choices=settings.PROVISIONING_DURATIONS)
  41. def get_use_class(self):
  42. return GoodUse
  43. def monthly_provision(self):
  44. return self.price/self.provisioning_duration.days*(365.25/12)
  45. class Meta:
  46. verbose_name = 'Bien'
  47. class AbstractUse(models.Model):
  48. share = models.FloatField(validators=[less_than_one])
  49. service = models.ForeignKey('Service')
  50. class Meta:
  51. abstract = True
  52. def clean(self):
  53. if hasattr(self, 'resource'):
  54. if (self.resource.used(except_by=self.service) + self.share) > 1:
  55. raise ValidationError(
  56. "Cannot use more than 100% of {})".format(self.resource))
  57. class CostUse(AbstractUse):
  58. resource = models.ForeignKey(Cost)
  59. def cost_share(self):
  60. return self.share*self.resource.price
  61. def unit_cost_share(self):
  62. subscriptions_count = self.service.subscriptions_count
  63. if subscriptions_count == 0:
  64. return 0
  65. else:
  66. return self.cost_share()/self.service.subscriptions_count
  67. class GoodUse(AbstractUse):
  68. resource = models.ForeignKey(Good)
  69. def monthly_provision_share(self):
  70. return self.real_share()*self.resource.monthly_provision()
  71. def unit_monthly_provision_share(self):
  72. subscriptions_count = self.service.subscriptions_count
  73. monthly_share = self.monthly_provision_share()
  74. if subscriptions_count == 0:
  75. return 0
  76. else:
  77. return monthly_share/subscriptions_count
  78. class Service(AbstractItem):
  79. """ A service we sell
  80. (considered monthly)
  81. """
  82. costs = models.ManyToManyField(
  83. Cost,
  84. through=CostUse,
  85. related_name='using_services')
  86. goods = models.ManyToManyField(
  87. Good,
  88. through=GoodUse,
  89. related_name='using_services')
  90. # services = models.ManyToMany('Service') #TODO
  91. subscriptions_count = models.PositiveIntegerField(default=0)
  92. def get_absolute_url(self):
  93. return reverse('detail-service', kwargs={'pk': self.pk})