diff --git a/cotisations/forms.py b/cotisations/forms.py index 76a67975..354da1f1 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -38,6 +38,7 @@ ArticleForm, BanqueForm, PaiementForm permettent aux admin d'ajouter, from __future__ import unicode_literals from django import forms +from django.db.models import Q from django.forms import ModelForm, Form from django.core.validators import MinValueValidator from .models import Article, Paiement, Facture, Banque @@ -90,10 +91,24 @@ class CreditSoldeForm(NewFactureForm): montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True) -class SelectArticleForm(Form): +class SelectUserArticleForm(Form): """Selection d'un article lors de la creation d'une facture""" article = forms.ModelChoiceField( - queryset=Article.objects.all(), + queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Adherent')), + label="Article", + required=True + ) + quantity = forms.IntegerField( + label="Quantité", + validators=[MinValueValidator(1)], + required=True + ) + + +class SelectClubArticleForm(Form): + """Selection d'un article lors de la creation d'une facture""" + article = forms.ModelChoiceField( + queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Club')), label="Article", required=True ) diff --git a/cotisations/migrations/0025_article_type_user.py b/cotisations/migrations/0025_article_type_user.py new file mode 100644 index 00000000..5ad329fa --- /dev/null +++ b/cotisations/migrations/0025_article_type_user.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-27 03:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0024_auto_20171015_2033'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='type_user', + field=models.CharField(choices=[('Adherent', 'Adherent'), ('Club', 'Club'), ('All', 'All')], default='All', max_length=255), + ), + ] diff --git a/cotisations/migrations/0026_auto_20171028_0126.py b/cotisations/migrations/0026_auto_20171028_0126.py new file mode 100644 index 00000000..436e0574 --- /dev/null +++ b/cotisations/migrations/0026_auto_20171028_0126.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-27 23:26 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def create_type(apps, schema_editor): + Cotisation = apps.get_model('cotisations', 'Cotisation') + Vente = apps.get_model('cotisations', 'Vente') + Article = apps.get_model('cotisations', 'Article') + db_alias = schema_editor.connection.alias + articles = Article.objects.using(db_alias).all() + ventes = Vente.objects.using(db_alias).all() + cotisations = Cotisation.objects.using(db_alias).all() + for article in articles: + if article.iscotisation: + article.type_cotisation='All' + article.save(using=db_alias) + for vente in ventes: + if vente.iscotisation: + vente.type_cotisation='All' + vente.save(using=db_alias) + for cotisation in cotisations: + cotisation.type_cotisation='All' + cotisation.save(using=db_alias) + +def delete_type(apps, schema_editor): + Vente = apps.get_model('cotisations', 'Vente') + Article = apps.get_model('cotisations', 'Article') + db_alias = schema_editor.connection.alias + articles = Articles.objects.using(db_alias).all() + ventes = Vente.objects.using(db_alias).all() + for article in articles: + if article.type_cotisation: + article.iscotisation=True + else: + article.iscotisation=False + article.save(using=db_alias) + for vente in ventes: + if vente.iscotisation: + vente.iscotisation=True + else: + vente.iscotisation=False + vente.save(using=db_alias) + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0025_article_type_user'), + ] + + operations = [ + migrations.AddField( + model_name='article', + name='type_cotisation', + field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], default=None, max_length=255, null=True), + ), + migrations.AddField( + model_name='cotisation', + name='type_cotisation', + field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255), + ), + migrations.AddField( + model_name='vente', + name='type_cotisation', + field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, null=True), + ), + migrations.RunPython(create_type, delete_type), + migrations.RemoveField( + model_name='article', + name='iscotisation', + ), + migrations.RemoveField( + model_name='vente', + name='iscotisation', + ), + ] diff --git a/cotisations/migrations/0027_auto_20171029_1156.py b/cotisations/migrations/0027_auto_20171029_1156.py new file mode 100644 index 00000000..8a9a4f0c --- /dev/null +++ b/cotisations/migrations/0027_auto_20171029_1156.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-29 10:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0026_auto_20171028_0126'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='name', + field=models.CharField(max_length=255), + ), + ] diff --git a/cotisations/models.py b/cotisations/models.py index 0b3aef35..090636be 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -47,6 +47,7 @@ from __future__ import unicode_literals from dateutil.relativedelta import relativedelta from django.db import models +from django.db.models import Q from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.forms import ValidationError @@ -127,15 +128,26 @@ class Vente(models.Model): iscotisation""" PRETTY_NAME = "Ventes effectuées" + COTISATION_TYPE = ( + ('Connexion', 'Connexion'), + ('Adhesion', 'Adhesion'), + ('All', 'All'), + ) + facture = models.ForeignKey('Facture', on_delete=models.CASCADE) number = models.IntegerField(validators=[MinValueValidator(1)]) name = models.CharField(max_length=255) prix = models.DecimalField(max_digits=5, decimal_places=2) - iscotisation = models.BooleanField() duration = models.PositiveIntegerField( help_text="Durée exprimée en mois entiers", blank=True, null=True) + type_cotisation = models.CharField( + choices=COTISATION_TYPE, + blank=True, + null=True, + max_length=255 + ) def prix_total(self): """Renvoie le prix_total de self (nombre*prix)""" @@ -155,22 +167,26 @@ class Vente(models.Model): """Update et crée l'objet cotisation associé à une facture, prend en argument l'user, la facture pour la quantitéi, et l'article pour la durée""" - if not hasattr(self, 'cotisation'): + if not hasattr(self, 'cotisation') and self.type_cotisation: cotisation = Cotisation(vente=self) + cotisation.type_cotisation = self.type_cotisation if date_start: - end_adhesion = Cotisation.objects.filter( + end_cotisation = Cotisation.objects.filter( vente__in=Vente.objects.filter( facture__in=Facture.objects.filter( user=self.facture.user ).exclude(valid=False)) + ).filter(Q(type_cotisation='All') | Q(type_cotisation=self.type_cotisation) ).filter( date_start__lt=date_start ).aggregate(Max('date_end'))['date_end__max'] + elif self.type_cotisation=="Adhesion": + end_cotisation = self.facture.user.end_adhesion() else: - end_adhesion = self.facture.user.end_adhesion() + end_cotisation = self.facture.user.end_connexion() date_start = date_start or timezone.now() - end_adhesion = end_adhesion or date_start - date_max = max(end_adhesion, date_start) + end_cotisation = end_cotisation or date_start + date_max = max(end_cotisation, date_start) cotisation.date_start = date_max cotisation.date_end = cotisation.date_start + relativedelta( months=self.duration*self.number @@ -179,7 +195,7 @@ class Vente(models.Model): def save(self, *args, **kwargs): # On verifie que si iscotisation, duration est présent - if self.iscotisation and not self.duration: + if self.type_cotisation and not self.duration: raise ValidationError("Cotisation et durée doivent être présents\ ensembles") self.update_cotisation() @@ -197,7 +213,7 @@ def vente_post_save(sender, **kwargs): if hasattr(vente, 'cotisation'): vente.cotisation.vente = vente vente.cotisation.save() - if vente.iscotisation: + if vente.type_cotisation: vente.create_cotis() vente.cotisation.save() user = vente.facture.user @@ -209,7 +225,7 @@ 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: + if vente.type_cotisation: user = vente.facture.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) @@ -219,18 +235,47 @@ class Article(models.Model): et duree si c'est une cotisation""" PRETTY_NAME = "Articles en vente" - name = models.CharField(max_length=255, unique=True) + USER_TYPES = ( + ('Adherent', 'Adherent'), + ('Club', 'Club'), + ('All', 'All'), + ) + + COTISATION_TYPE = ( + ('Connexion', 'Connexion'), + ('Adhesion', 'Adhesion'), + ('All', 'All'), + ) + + name = models.CharField(max_length=255) prix = models.DecimalField(max_digits=5, decimal_places=2) - iscotisation = models.BooleanField() duration = models.PositiveIntegerField( help_text="Durée exprimée en mois entiers", blank=True, null=True, validators=[MinValueValidator(0)]) + type_user = models.CharField( + choices=USER_TYPES, + default='All', + max_length=255 + ) + type_cotisation = models.CharField( + choices=COTISATION_TYPE, + default=None, + blank=True, + null=True, + max_length=255 + ) + + unique_together = ('name', 'type_user') def clean(self): if self.name.lower() == "solde": raise ValidationError("Solde est un nom d'article invalide") + if self.type_cotisation and not self.duration: + raise ValidationError( + "La durée est obligatoire si il s'agit d'une cotisation" + ) def __str__(self): return self.name @@ -275,7 +320,17 @@ class Cotisation(models.Model): """Objet cotisation, debut et fin, relié en onetoone à une vente""" PRETTY_NAME = "Cotisations" + COTISATION_TYPE = ( + ('Connexion', 'Connexion'), + ('Adhesion', 'Adhesion'), + ('All', 'All'), + ) + vente = models.OneToOneField('Vente', on_delete=models.CASCADE, null=True) + type_cotisation = models.CharField( + choices=COTISATION_TYPE, + max_length=255, + ) date_start = models.DateTimeField() date_end = models.DateTimeField() diff --git a/cotisations/templates/cotisations/aff_article.html b/cotisations/templates/cotisations/aff_article.html index b756f746..3a0b21f6 100644 --- a/cotisations/templates/cotisations/aff_article.html +++ b/cotisations/templates/cotisations/aff_article.html @@ -27,8 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., Article Prix - Cotisation + Type Cotisation Durée (mois) + Article pour @@ -36,8 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ article.name }} {{ article.prix }} - {{ article.iscotisation }} + {{ article.type_cotisation }} {{ article.duration }} + {{ article.type_user }} {% if is_trez %} diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index 59d558bf..81feb0bf 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Prix total {% include "buttons/sort.html" with prefix='cotis' col='paiement' text='Moyen de paiement' %} {% include "buttons/sort.html" with prefix='cotis' col='date' text='Date' %} - + {% include "buttons/sort.html" with prefix='cotis' col='id' text='Id facture' %} @@ -46,17 +46,19 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ facture.prix_total }} {{ facture.paiement }} {{ facture.date }} + {{ facture.id }} {% if is_cableur %}