123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- from django.db import models
- from django.contrib.contenttypes.fields import GenericForeignKey
- from django.contrib.contenttypes.models import ContentType
- from django.core.validators import MaxValueValidator
- from django.core.urlresolvers import reverse
- from django.core.exceptions import ValidationError
- from django.urls import reverse
- from django.core.exceptions import PermissionDenied
- from django.utils import timezone
- from djadhere.utils import is_overlapping, get_active_filter
- class Payment(models.Model):
- TRANSFERT = 0
- WITHDRAWAL = 1
- PAYMENT_CHOICES = (
- (TRANSFERT, 'Virement'),
- (WITHDRAWAL, 'Prélèvement'),
- )
- ADHESION = 0
- SERVICE = 1
- limit = models.Q(app_label='adhesions', model='adhesion') \
- | models.Q(app_label='services', model='service')
- reason_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,
- limit_choices_to=limit)
- reason_id = models.PositiveIntegerField()
- reason = GenericForeignKey('reason_type', 'reason_id')
- amount = models.DecimalField(max_digits=9, decimal_places=2, verbose_name='Montant')
- period = models.PositiveIntegerField(validators=[MaxValueValidator(12)],
- verbose_name='Période (mois) (0 si non récurrent)')
- payment_method = models.IntegerField(choices=PAYMENT_CHOICES,
- verbose_name='Méthode de paiement')
- start = models.DateField(verbose_name='Date de paiement ou de début de paiement')
- end = models.DateField(null=True, blank=True, verbose_name='Date de fin de paiement (si récurrent)')
- validated = models.BooleanField(default=False, verbose_name='Paiement validé')
- class Meta:
- verbose_name = 'paiement'
- permissions = (
- ('validate_payment', 'Peut valider les paiements'),
- )
- @property
- def active(self):
- # Contrairement aux allocations d’IP, un paiement est considéré actif même s’il commence dans le futur
- # Seul les paiements récurrents peuvent être actifs
- if self.period == 0:
- return None
- today = timezone.localtime(timezone.now()).date()
- return self.end is None or (self.start <= today and self.end > today)
- @property
- def type(self):
- if self.reason_type.app_label == 'adhesions' \
- and self.reason_type.model == 'adhesion':
- return Payment.ADHESION
- if self.reason_type.app_label == 'services' \
- and self.reason_type.model == 'service':
- return Payment.SERVICE
- def type_verbose(self):
- if self.type == Payment.ADHESION:
- return 'Cotisation'
- if self.type == Payment.SERVICE:
- return 'Service'
- type_verbose.short_description = 'Type'
- def period_verbose(self):
- if self.period == 0:
- return 'non récurrent'
- elif self.period == 1:
- return 'mensuel'
- elif self.period == 3:
- return 'trimestriel'
- elif self.period == 6:
- return 'biannuel'
- elif self.period == 12:
- return 'annuel'
- else:
- return '%d mois' % self.period
- # FIXME
- @property
- def adherent(self):
- return self.get_adhesion().adherent
- def get_adhesion(self):
- if self.reason_type.app_label == 'adhesions' \
- and self.reason_type.model == 'adhesion':
- return self.reason
- if self.reason_type.app_label == 'services' \
- and self.reason_type.model == 'service':
- return self.reason.adhesion
- get_adhesion.short_description = 'Adhésion'
- # Penser à appele la méthode save !
- def stop(self):
- if not self.active:
- raise PermissionDenied
- today = timezone.localtime(timezone.now()).date()
- self.end = max(self.start, today)
- def clean(self):
- super().clean()
- # S’il s’agit d’un paiement non récurrent, le champ end doit rester blanc
- if self.period == 0 and self.end:
- raise ValidationError({'end': "Un paiement non récurrent ne doit pas avoir de date de fin."})
- # Vérification de la cohérence des champs start et end
- if self.end and self.start > self.end:
- raise ValidationError("La date de début de paiement doit être antérieur à la date de fin de paiement.")
- # Vérification de l’absence de chevauchement avec une période existante
- # FIXME ne fonctionne pas vraiment via les inlines…
- if self.reason:
- payments = Payment.objects.filter(reason_type=self.reason_type, reason_id=self.reason_id)
- if is_overlapping(self, payments):
- raise ValidationError("Les périodes de paiement ne doivent pas se chevaucher.")
- def get_absolute_url(self):
- return reverse('payment-detail', kwargs={'pk': self.pk})
- def __str__(self):
- s = str(self.amount) + '€'
- if self.period:
- if self.period == 1:
- s += '/mois'
- elif self.period == 12:
- s += '/an'
- else:
- s += '/%d mois' % self.period
- if self.payment_method == self.TRANSFERT:
- s += ' (virement)'
- elif self.payment_method == self.WITHDRAWAL:
- s += ' (prélèvement)'
- return s
|