diff --git a/cotisations/admin.py b/cotisations/admin.py index b3f91854..8186e4e3 100644 --- a/cotisations/admin.py +++ b/cotisations/admin.py @@ -30,26 +30,33 @@ from .models import Facture, Article, Banque, Paiement, Cotisation, Vente class FactureAdmin(VersionAdmin): + """Class admin d'une facture, tous les champs""" pass class VenteAdmin(VersionAdmin): + """Class admin d'une vente, tous les champs (facture related)""" pass class ArticleAdmin(VersionAdmin): + """Class admin d'un article en vente""" pass class BanqueAdmin(VersionAdmin): + """Class admin de la liste des banques (facture related)""" pass class PaiementAdmin(VersionAdmin): + """Class admin d'un moyen de paiement (facture related""" pass class CotisationAdmin(VersionAdmin): + """Class admin d'une cotisation (date de debut et de fin), + Vente related""" pass diff --git a/cotisations/forms.py b/cotisations/forms.py index b3742088..76a67975 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -19,14 +19,28 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Forms de l'application cotisation de re2o. Dépendance avec les models, +importé par les views. +Permet de créer une nouvelle facture pour un user (NewFactureForm), +et de l'editer (soit l'user avec EditFactureForm, +soit le trésorier avec TrezEdit qui a plus de possibilités que self +notamment sur le controle trésorier) + +SelectArticleForm est utilisée lors de la creation d'une facture en +parrallèle de NewFacture pour le choix des articles désirés. +(la vue correspondante est unique) + +ArticleForm, BanqueForm, PaiementForm permettent aux admin d'ajouter, +éditer ou supprimer une banque/moyen de paiement ou un article +""" from __future__ import unicode_literals from django import forms from django.forms import ModelForm, Form -from django import forms from django.core.validators import MinValueValidator -from .models import Article, Paiement, Facture, Banque, Vente +from .models import Article, Paiement, Facture, Banque class NewFactureForm(ModelForm): diff --git a/cotisations/models.py b/cotisations/models.py index 6388b38c..54843076 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -20,20 +20,39 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Definition des models bdd pour les factures et cotisation. +Pièce maitresse : l'ensemble du code intelligent se trouve ici, +dans les clean et save des models ainsi que de leur methodes supplémentaires. + +Facture : reliée à un user, elle a un moyen de paiement, une banque (option), +une ou plusieurs ventes + +Article : liste des articles en vente, leur prix, etc + +Vente : ensemble des ventes effectuées, reliées à une facture (foreignkey) + +Banque : liste des banques existantes + +Cotisation : objets de cotisation, contenant un début et une fin. Reliées +aux ventes, en onetoone entre une vente et une cotisation. +Crées automatiquement au save des ventes. + +Post_save et Post_delete : sychronisation des services et régénération +des services d'accès réseau (ex dhcp) lors de la vente d'une cotisation +par exemple +""" from __future__ import unicode_literals +from dateutil.relativedelta import relativedelta from django.db import models - from django.db.models.signals import post_save, post_delete from django.dispatch import receiver -from dateutil.relativedelta import relativedelta from django.forms import ValidationError from django.core.validators import MinValueValidator - from django.db.models import Max from django.utils import timezone - from machines.models import regen @@ -47,32 +66,34 @@ class Facture(models.Model): user = models.ForeignKey('users.User', on_delete=models.PROTECT) paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) banque = models.ForeignKey( - 'Banque', - on_delete=models.PROTECT, - blank=True, - null=True) + 'Banque', + on_delete=models.PROTECT, + blank=True, + null=True) cheque = models.CharField(max_length=255, blank=True) date = models.DateTimeField(auto_now_add=True) valid = models.BooleanField(default=True) control = models.BooleanField(default=False) def prix(self): + """Renvoie le prix brut sans les quantités. Méthode + dépréciée""" prix = Vente.objects.filter( - facture=self - ).aggregate(models.Sum('prix'))['prix__sum'] + facture=self + ).aggregate(models.Sum('prix'))['prix__sum'] return prix def prix_total(self): """Prix total : somme des produits prix_unitaire et quantité des ventes de l'objet""" return Vente.objects.filter( - facture=self - ).aggregate( + facture=self + ).aggregate( total=models.Sum( models.F('prix')*models.F('number'), output_field=models.FloatField() - ) - )['total'] + ) + )['total'] def name(self): """String, somme des name des ventes de self""" @@ -95,6 +116,7 @@ def facture_post_save(sender, **kwargs): @receiver(post_delete, sender=Facture) def facture_post_delete(sender, **kwargs): + """Après la suppression d'une facture, on synchronise l'user ldap""" user = kwargs['instance'].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) @@ -111,19 +133,22 @@ class Vente(models.Model): prix = models.DecimalField(max_digits=5, decimal_places=2) iscotisation = models.BooleanField() duration = models.IntegerField( - help_text="Durée exprimée en mois entiers", - blank=True, - null=True) + help_text="Durée exprimée en mois entiers", + blank=True, + null=True) def prix_total(self): """Renvoie le prix_total de self (nombre*prix)""" return self.prix*self.number def update_cotisation(self): + """Mets à jour l'objet related cotisation de la vente, si + il existe : update la date de fin à partir de la durée de + la vente""" if hasattr(self, 'cotisation'): cotisation = self.cotisation cotisation.date_end = cotisation.date_start + relativedelta( - months=self.duration*self.number) + months=self.duration*self.number) return def create_cotis(self, date_start=False): @@ -134,13 +159,13 @@ class Vente(models.Model): cotisation = Cotisation(vente=self) if date_start: end_adhesion = Cotisation.objects.filter( - vente__in=Vente.objects.filter( - facture__in=Facture.objects.filter( - user=self.facture.user - ).exclude(valid=False)) - ).filter( + vente__in=Vente.objects.filter( + facture__in=Facture.objects.filter( + user=self.facture.user + ).exclude(valid=False)) + ).filter( date_start__lt=date_start - ).aggregate(Max('date_end'))['date_end__max'] + ).aggregate(Max('date_end'))['date_end__max'] else: end_adhesion = self.facture.user.end_adhesion() date_start = date_start or timezone.now() @@ -148,8 +173,8 @@ class Vente(models.Model): date_max = max(end_adhesion, date_start) cotisation.date_start = date_max cotisation.date_end = cotisation.date_start + relativedelta( - months=self.duration*self.number - ) + months=self.duration*self.number + ) return def save(self, *args, **kwargs): @@ -181,6 +206,8 @@ def vente_post_save(sender, **kwargs): @receiver(post_delete, sender=Vente) def vente_post_delete(sender, **kwargs): + """Après suppression d'une vente, on synchronise l'user ldap (ex + suppression d'une cotisation""" vente = kwargs['instance'] if vente.iscotisation: user = vente.facture.user @@ -258,6 +285,7 @@ class Cotisation(models.Model): @receiver(post_save, sender=Cotisation) def cotisation_post_save(sender, **kwargs): + """Après modification d'une cotisation, regeneration des services""" regen('dns') regen('dhcp') regen('mac_ip_list') @@ -266,6 +294,7 @@ def cotisation_post_save(sender, **kwargs): @receiver(post_delete, sender=Cotisation) def vente_post_delete(sender, **kwargs): + """Après suppression d'une vente, régénération des services""" cotisation = kwargs['instance'] regen('mac_ip_list') regen('mailing')