models.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from django.db import models
  2. from django.contrib.contenttypes.fields import GenericForeignKey
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.core.validators import MaxValueValidator
  5. from django.core.urlresolvers import reverse
  6. from django.core.exceptions import ValidationError
  7. from django.urls import reverse
  8. from django.core.exceptions import PermissionDenied
  9. from django.utils import timezone
  10. from djadhere.utils import is_overlapping, get_active_filter
  11. class Payment(models.Model):
  12. TRANSFERT = 0
  13. WITHDRAWAL = 1
  14. PAYMENT_CHOICES = (
  15. (TRANSFERT, 'Virement'),
  16. (WITHDRAWAL, 'Prélèvement'),
  17. )
  18. ADHESION = 0
  19. SERVICE = 1
  20. limit = models.Q(app_label='adhesions', model='adhesion') \
  21. | models.Q(app_label='services', model='service')
  22. reason_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,
  23. limit_choices_to=limit)
  24. reason_id = models.PositiveIntegerField()
  25. reason = GenericForeignKey('reason_type', 'reason_id')
  26. amount = models.DecimalField(max_digits=9, decimal_places=2, verbose_name='Montant')
  27. period = models.PositiveIntegerField(validators=[MaxValueValidator(12)],
  28. verbose_name='Période (mois) (0 si non récurrent)')
  29. payment_method = models.IntegerField(choices=PAYMENT_CHOICES,
  30. verbose_name='Méthode de paiement')
  31. start = models.DateField(verbose_name='Date de paiement ou de début de paiement')
  32. end = models.DateField(null=True, blank=True, verbose_name='Date de fin de paiement (si récurrent)')
  33. validated = models.BooleanField(default=False, verbose_name='Paiement validé')
  34. class Meta:
  35. verbose_name = 'paiement'
  36. permissions = (
  37. ('validate_payment', 'Peut valider les paiements'),
  38. )
  39. @property
  40. def active(self):
  41. # Contrairement aux allocations d’IP, un paiement est considéré actif même s’il commence dans le futur
  42. # Seul les paiements récurrents peuvent être actifs
  43. if self.period == 0:
  44. return None
  45. today = timezone.localtime(timezone.now()).date()
  46. return self.end is None or (self.start <= today and self.end > today)
  47. @property
  48. def type(self):
  49. if self.reason_type.app_label == 'adhesions' \
  50. and self.reason_type.model == 'adhesion':
  51. return Payment.ADHESION
  52. if self.reason_type.app_label == 'services' \
  53. and self.reason_type.model == 'service':
  54. return Payment.SERVICE
  55. def type_verbose(self):
  56. if self.type == Payment.ADHESION:
  57. return 'Cotisation'
  58. if self.type == Payment.SERVICE:
  59. return 'Service'
  60. type_verbose.short_description = 'Type'
  61. def period_verbose(self):
  62. if self.period == 0:
  63. return 'non récurrent'
  64. elif self.period == 1:
  65. return 'mensuel'
  66. elif self.period == 3:
  67. return 'trimestriel'
  68. elif self.period == 6:
  69. return 'biannuel'
  70. elif self.period == 12:
  71. return 'annuel'
  72. else:
  73. return '%d mois' % self.period
  74. # FIXME
  75. @property
  76. def adherent(self):
  77. return self.get_adhesion().adherent
  78. def get_adhesion(self):
  79. if self.reason_type.app_label == 'adhesions' \
  80. and self.reason_type.model == 'adhesion':
  81. return self.reason
  82. if self.reason_type.app_label == 'services' \
  83. and self.reason_type.model == 'service':
  84. return self.reason.adhesion
  85. get_adhesion.short_description = 'Adhésion'
  86. # Penser à appele la méthode save !
  87. def stop(self):
  88. if not self.active:
  89. raise PermissionDenied
  90. today = timezone.localtime(timezone.now()).date()
  91. self.end = max(self.start, today)
  92. def clean(self):
  93. super().clean()
  94. # S’il s’agit d’un paiement non récurrent, le champ end doit rester blanc
  95. if self.period == 0 and self.end:
  96. raise ValidationError({'end': "Un paiement non récurrent ne doit pas avoir de date de fin."})
  97. # Vérification de la cohérence des champs start et end
  98. if self.end and self.start > self.end:
  99. raise ValidationError("La date de début de paiement doit être antérieur à la date de fin de paiement.")
  100. # Vérification de l’absence de chevauchement avec une période existante
  101. # FIXME ne fonctionne pas vraiment via les inlines…
  102. if self.reason:
  103. payments = Payment.objects.filter(reason_type=self.reason_type, reason_id=self.reason_id)
  104. if is_overlapping(self, payments):
  105. raise ValidationError("Les périodes de paiement ne doivent pas se chevaucher.")
  106. def get_absolute_url(self):
  107. return reverse('payment-detail', kwargs={'pk': self.pk})
  108. def __str__(self):
  109. s = str(self.amount) + '€'
  110. if self.period:
  111. if self.period == 1:
  112. s += '/mois'
  113. elif self.period == 12:
  114. s += '/an'
  115. else:
  116. s += '/%d mois' % self.period
  117. if self.payment_method == self.TRANSFERT:
  118. s += ' (virement)'
  119. elif self.payment_method == self.WITHDRAWAL:
  120. s += ' (prélèvement)'
  121. return s