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 djadhere.utils import is_overlapping class Payment(models.Model): TRANSFERT = 0 WITHDRAWAL = 1 PAYMENT_CHOICES = ( (TRANSFERT, 'Virement'), (WITHDRAWAL, 'Prélèvement'), ) limit = models.Q(app_label='adhesions', model='Adherent') \ | 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'), ) def payment_type_verbose(self): if self.reason_type.app_label == 'adhesions' \ and self.reason_type.model == 'adherent': return 'Cotisation' if self.reason_type.app_label == 'services' \ and self.reason_type.model == 'service': return 'Service (%s)' % self.reason.service_type payment_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 # Note: 'adherent' is already used as the related_query_name # of the GenericRelation on Adherent def get_adherent(self): if self.reason_type.app_label == 'adhesions' \ and self.reason_type.model == 'adherent': return self.reason if self.reason_type.app_label == 'services' \ and self.reason_type.model == 'service': return self.reason.adherent get_adherent.short_description = 'Adhérent' 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 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 __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