models.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from django.db import models
  2. from django.core.validators import MinValueValidator, MaxValueValidator
  3. from django.core.exceptions import ValidationError
  4. from django.urls import reverse
  5. class CurrentPaymentManager(models.Manager):
  6. def get_queryset(self):
  7. qs = super().get_queryset()
  8. qs = qs.annotate(
  9. payment_method=models.Subquery(
  10. PaymentUpdate.objects.filter(payment=models.OuterRef('pk'), validated=True)
  11. .values('payment_method')[:1]
  12. )
  13. )
  14. qs = qs.annotate(
  15. active=models.Case(
  16. models.When(payment_method__isnull=True, then=None),
  17. models.When(payment_method=PaymentUpdate.STOP, then=False),
  18. default=True,
  19. output_field=models.NullBooleanField()
  20. )
  21. )
  22. return qs
  23. class RecurringPayment(models.Model):
  24. objects = CurrentPaymentManager()
  25. @property
  26. def debtor(self):
  27. if hasattr(self, 'adhesion'):
  28. return self.adhesion
  29. if hasattr(self, 'service'):
  30. return self.service.adhesion
  31. def payment_type(self):
  32. if hasattr(self, 'adhesion'):
  33. return 'Adhésion'
  34. if hasattr(self, 'service'):
  35. return 'Service'
  36. payment_type.short_description = 'Type'
  37. def payment_object(self):
  38. return self.adhesion if hasattr(self, 'adhesion') else self.service
  39. def get_absolute_url(self):
  40. return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=(self.pk,))
  41. def get_current_payment(self):
  42. return self.updates.filter(validated=True).first()
  43. def get_current_payment_display(self):
  44. current = self.get_current_payment()
  45. if current:
  46. return str(current)
  47. else:
  48. return 'non renseignée'
  49. class Meta:
  50. verbose_name = 'paiement récurrent'
  51. verbose_name_plural = 'paiements récurrents'
  52. def __str__(self):
  53. if hasattr(self, 'adhesion'):
  54. return "Cotisation %s" % self.adhesion
  55. else:
  56. return "Contribution %s" % self.service
  57. class PaymentUpdate(models.Model):
  58. STOP = 0
  59. FREE = 1
  60. DEBIT = 2
  61. TRANSFER = 3
  62. CASH = 4
  63. INVOICE = 5
  64. PAYMENT_CHOICES = (
  65. (DEBIT, 'Prélèvement'),
  66. (TRANSFER, 'Virement'),
  67. (CASH, 'Liquide'),
  68. (INVOICE, 'Facture'),
  69. (FREE, 'Gratuit'),
  70. (STOP, 'Arrêt'),
  71. )
  72. created = models.DateTimeField(auto_now_add=True)
  73. payment = models.ForeignKey(RecurringPayment, related_name='updates', on_delete=models.CASCADE)
  74. amount = models.DecimalField(max_digits=9, decimal_places=2, verbose_name='Montant')
  75. period = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(12)],
  76. verbose_name='Période (mois)')
  77. payment_method = models.IntegerField(choices=PAYMENT_CHOICES, default=DEBIT,
  78. verbose_name='Méthode de paiement')
  79. start = models.DateTimeField(verbose_name='Date')
  80. month_day = models.PositiveIntegerField(validators=[MinValueValidator(1), MaxValueValidator(25)],
  81. verbose_name='Jour souhaité de prélèvement',
  82. null=True, blank=True)
  83. validated = models.BooleanField(default=False, verbose_name='Saisie bancaire effectuée')
  84. class Meta:
  85. verbose_name = 'paiement'
  86. ordering = ('-start',)
  87. def clean(self):
  88. super().clean()
  89. errors = {}
  90. if self.payment_method == PaymentUpdate.STOP and self.amount:
  91. errors.update({'amount': "Pour l’arrêt d’un paiement, le montant doit être nul."})
  92. if errors:
  93. raise ValidationError(errors)
  94. def period_verbose(self):
  95. if self.period == 0:
  96. return 'non récurrent'
  97. elif self.period == 1:
  98. return 'mensuel'
  99. elif self.period == 3:
  100. return 'trimestriel'
  101. elif self.period == 6:
  102. return 'biannuel'
  103. elif self.period == 12:
  104. return 'annuel'
  105. else:
  106. return '%d mois' % self.period
  107. def __str__(self):
  108. if self.payment_method == self.STOP:
  109. return 'paiement arrêté'
  110. if self.payment_method == self.FREE:
  111. return 'libre'
  112. s = str(self.amount) + '€'
  113. if self.period:
  114. if self.period == 1:
  115. s += '/mois'
  116. elif self.period == 12:
  117. s += '/an'
  118. else:
  119. s += '/%d mois' % self.period
  120. if self.payment_method == self.DEBIT:
  121. s += ' (prélèvement)'
  122. elif self.payment_method == self.TRANSFER:
  123. s += ' (virement)'
  124. elif self.payment_method == self.CASH:
  125. s += ' (liquide)'
  126. elif self.payment_method == self.INVOICE:
  127. s += ' (sur facture)'
  128. return s