diff --git a/cotisations/forms.py b/cotisations/forms.py
index 5845611a..bbe50b69 100644
--- a/cotisations/forms.py
+++ b/cotisations/forms.py
@@ -26,9 +26,8 @@ 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
+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)
@@ -40,7 +39,7 @@ 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 django.core.validators import MinValueValidator,MaxValueValidator
from .models import Article, Paiement, Facture, Banque
from re2o.field_permissions import FieldPermissionFormMixin
@@ -246,3 +245,37 @@ class DelBanqueForm(Form):
self.fields['banques'].queryset = instances
else:
self.fields['banques'].queryset = Banque.objects.all()
+
+
+class NewFactureSoldeForm(NewFactureForm):
+ """Creation d'une facture, moyen de paiement, banque et numero
+ de cheque"""
+ def __init__(self, *args, **kwargs):
+ prefix = kwargs.pop('prefix', self.Meta.model.__name__)
+ self.fields['cheque'].required = False
+ self.fields['banque'].required = False
+ self.fields['cheque'].label = 'Numero de chèque'
+ self.fields['banque'].empty_label = "Non renseigné"
+ self.fields['paiement'].empty_label = "Séléctionner\
+ une bite de paiement"
+ paiement_list = Paiement.objects.filter(type_paiement=1)
+ if paiement_list:
+ self.fields['paiement'].widget\
+ .attrs['data-cheque'] = paiement_list.first().id
+
+ class Meta:
+ model = Facture
+ fields = ['paiement', 'banque']
+
+
+ def clean(self):
+ cleaned_data = super(NewFactureSoldeForm, self).clean()
+ paiement = cleaned_data.get("paiement")
+ cheque = cleaned_data.get("cheque")
+ banque = cleaned_data.get("banque")
+ if not paiement:
+ raise forms.ValidationError("Le moyen de paiement est obligatoire")
+ elif paiement.type_paiement == "check" and not (cheque and banque):
+ raise forms.ValidationError("Le numéro de chèque et\
+ la banque sont obligatoires.")
+ return cleaned_data
diff --git a/cotisations/templates/cotisations/new_facture.html b/cotisations/templates/cotisations/new_facture.html
index f2586e8b..9d466cee 100644
--- a/cotisations/templates/cotisations/new_facture.html
+++ b/cotisations/templates/cotisations/new_facture.html
@@ -34,6 +34,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+
+
+
+{% endblock %}
+
diff --git a/cotisations/urls.py b/cotisations/urls.py
index 2a0c0163..cbeeb8eb 100644
--- a/cotisations/urls.py
+++ b/cotisations/urls.py
@@ -26,6 +26,7 @@ from django.conf.urls import url
import re2o
from . import views
+from . import payment
urlpatterns = [
url(r'^new_facture/(?P[0-9]+)$',
@@ -110,5 +111,9 @@ urlpatterns = [
views.control,
name='control'
),
+ url(r'^new_facture_solde/(?P[0-9]+)$',
+ views.new_facture_solde,
+ name='new_facture_solde'
+ ),
url(r'^$', views.index, name='index'),
]
diff --git a/cotisations/views.py b/cotisations/views.py
index d7d953c9..e338943e 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -29,6 +29,7 @@ import os
from django.urls import reverse
from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from django.core.validators import MaxValueValidator
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import messages
from django.db.models import ProtectedError
@@ -67,7 +68,9 @@ from .forms import (
NewFactureFormPdf,
SelectUserArticleForm,
SelectClubArticleForm,
- CreditSoldeForm
+ CreditSoldeForm,
+ NewFactureSoldeForm,
+ RechargeForm
)
from .tex import render_invoice
@@ -584,3 +587,98 @@ def index(request):
return render(request, 'cotisations/index.html', {
'facture_list': facture_list
})
+
+
+@login_required
+def new_facture_solde(request, userid):
+ """Creation d'une facture pour un user. Renvoie la liste des articles
+ et crée des factures dans un formset. Utilise un peu de js coté template
+ pour ajouter des articles.
+ Parse les article et boucle dans le formset puis save les ventes,
+ enfin sauve la facture parente.
+ TODO : simplifier cette fonction, déplacer l'intelligence coté models
+ Facture et Vente."""
+ user = request.user
+ facture = Facture(user=user)
+ paiement, _created = Paiement.objects.get_or_create(moyen='Solde')
+ facture.paiement = paiement
+ # Le template a besoin de connaitre les articles pour le js
+ article_list = Article.objects.filter(
+ Q(type_user='All') | Q(type_user=request.user.class_name)
+ )
+ if request.user.is_class_club:
+ article_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
+ else:
+ article_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
+ if article_formset.is_valid():
+ articles = article_formset
+ # Si au moins un article est rempli
+ if any(art.cleaned_data for art in articles):
+ options, _created = OptionalUser.objects.get_or_create()
+ user_solde = options.user_solde
+ solde_negatif = options.solde_negatif
+ # Si on paye par solde, que l'option est activée,
+ # on vérifie que le négatif n'est pas atteint
+ if user_solde:
+ prix_total = 0
+ for art_item in articles:
+ if art_item.cleaned_data:
+ prix_total += art_item.cleaned_data['article']\
+ .prix*art_item.cleaned_data['quantity']
+ if float(user.solde) - float(prix_total) < solde_negatif:
+ messages.error(request, "Le solde est insuffisant pour\
+ effectuer l'opération")
+ return redirect(reverse(
+ 'users:profil',
+ kwargs={'userid': userid}
+ ))
+ with transaction.atomic(), reversion.create_revision():
+ facture.save()
+ reversion.set_user(request.user)
+ reversion.set_comment("Création")
+ for art_item in articles:
+ if art_item.cleaned_data:
+ article = art_item.cleaned_data['article']
+ quantity = art_item.cleaned_data['quantity']
+ new_vente = Vente.objects.create(
+ facture=facture,
+ name=article.name,
+ prix=article.prix,
+ type_cotisation=article.type_cotisation,
+ duration=article.duration,
+ number=quantity
+ )
+ with transaction.atomic(), reversion.create_revision():
+ new_vente.save()
+ reversion.set_user(request.user)
+ reversion.set_comment("Création")
+ if any(art_item.cleaned_data['article'].type_cotisation
+ for art_item in articles if art_item.cleaned_data):
+ messages.success(
+ request,
+ "La cotisation a été prolongée\
+ pour l'adhérent %s jusqu'au %s" % (
+ user.pseudo, user.end_adhesion()
+ )
+ )
+ else:
+ messages.success(request, "La facture a été crée")
+ return redirect(reverse(
+ 'users:profil',
+ kwargs={'userid': userid}
+ ))
+ messages.error(
+ request,
+ u"Il faut au moins un article valide pour créer une facture"
+ )
+ return redirect(reverse(
+ 'users:profil',
+ kwargs={'userid': userid}
+ ))
+
+ return form({
+ 'venteform': article_formset,
+ 'articlelist': article_list
+ }, 'cotisations/new_facture_solde.html', request)
+
+
diff --git a/preferences/forms.py b/preferences/forms.py
index 7dda8620..2f79b6fb 100644
--- a/preferences/forms.py
+++ b/preferences/forms.py
@@ -48,6 +48,7 @@ class EditOptionalUserForm(ModelForm):
téléphone'
self.fields['user_solde'].label = 'Activation du solde pour\
les utilisateurs'
+ self.fields['max_recharge'].label = 'Rechargement max'
class EditOptionalMachineForm(ModelForm):
diff --git a/preferences/migrations/0028_auto_20180111_1129.py b/preferences/migrations/0028_auto_20180111_1129.py
new file mode 100644
index 00000000..c6c03719
--- /dev/null
+++ b/preferences/migrations/0028_auto_20180111_1129.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2018-01-11 10:29
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('preferences', '0027_merge_20180106_2019'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='optionaluser',
+ name='max_recharge',
+ field=models.DecimalField(decimal_places=2, default=100, max_digits=5),
+ ),
+ ]
diff --git a/preferences/models.py b/preferences/models.py
index 8dfc4260..1d4b9bd2 100644
--- a/preferences/models.py
+++ b/preferences/models.py
@@ -41,6 +41,11 @@ class OptionalUser(models.Model):
decimal_places=2,
default=0
)
+ max_recharge = models.DecimalField(
+ max_digits=5,
+ decimal_places=2,
+ default=100
+ )
gpg_fingerprint = models.BooleanField(default=True)
all_can_create = models.BooleanField(
default=False,
@@ -107,7 +112,10 @@ class OptionalUser(models.Model):
def clean(self):
"""Creation du mode de paiement par solde"""
if self.user_solde:
- cotisations.models.Paiement.objects.get_or_create(moyen="Solde")
+ p = cotisations.models.Paiement.objects.filter(moyen="Solde")
+ if not len(p):
+ c = cotisations.models.Paiement(moyen="Solde")
+ c.save()
class OptionalMachine(models.Model):
@@ -436,7 +444,14 @@ class AssoOption(models.Model):
blank=True,
null=True
)
-
+ PAYMENT = (
+ ('NONE', 'NONE'),
+ ('COMNPAY', 'COMNPAY'),
+ )
+ payment = models.CharField(max_length=255,
+ choices=PAYMENT,
+ default='NONE',
+ )
class Meta:
permissions = (
("view_assooption", "Peut voir les options de l'asso"),
diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html
index 7802929d..38649e16 100644
--- a/preferences/templates/preferences/display_preferences.html
+++ b/preferences/templates/preferences/display_preferences.html
@@ -54,6 +54,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Creations d'users par tous |
{{ useroptions.all_can_create }} |
+ {% if useroptions.user_solde %}
+ Rechargement max |
+ {{ useroptions.max_recharge }} |
+ {% endif %}
Préférences machines
@@ -159,7 +163,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Objet utilisateur de l'association |
{{ assooptions.utilisateur_asso }} |
+ Moyen de paiement automatique |
+ {{ assooptions.payment }} |
+
Messages personalisé dans les mails
diff --git a/users/models.py b/users/models.py
index 998678cd..1852c687 100644
--- a/users/models.py
+++ b/users/models.py
@@ -153,7 +153,7 @@ class UserManager(BaseUserManager):
user.set_password(password)
if su:
user.is_superuser=True
- user.save(using=self._db)
+ user.save(using=self._db)
return user
def create_user(self, pseudo, surname, email, password=None):
@@ -409,13 +409,11 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin):
options, _created = OptionalUser.objects.get_or_create()
user_solde = options.user_solde
if user_solde:
- solde_object, _created = Paiement.objects.get_or_create(
- moyen='Solde'
- )
+ solde_objects = Paiement.objects.filter(moyen='Solde')
somme_debit = Vente.objects.filter(
facture__in=Facture.objects.filter(
user=self,
- paiement=solde_object
+ paiement__in=solde_objects
)
).aggregate(
total=models.Sum(
diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html
index 0e5f30e4..8f399043 100644
--- a/users/templates/users/profil.html
+++ b/users/templates/users/profil.html
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}Profil{% endblock %}
{% block content %}
-{{ users.class_name }}
+{{ users.class_name }} : {{ users.surname }} {{users.name}}
@@ -135,13 +135,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if user_solde %}
Solde |
- {{ users.solde }} € |
-
+ {{ users.solde }} €
+
+
+ Recharger
+
+ |
{% endif %}
{% if users.shell %}
Shell |
{{ users.shell }} |
{% endif %}
+
{% if users.is_class_club %}
@@ -191,7 +196,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Aucune machine
{% endif %}
Cotisations
-
+
{% if facture_list %}
{% include "cotisations/aff_cotisations.html" with facture_list=facture_list %}
{% else %}