diff --git a/cotisations/acl.py b/cotisations/acl.py index 868e3411..4e147a82 100644 --- a/cotisations/acl.py +++ b/cotisations/acl.py @@ -25,6 +25,7 @@ Here are defined some functions to check acl on the application. """ +from django.utils.translation import ugettext as _ def can_view(user): """Check if an user can view the application. @@ -37,4 +38,4 @@ def can_view(user): viewing is granted and msg is a message (can be None). """ can = user.has_module_perms('cotisations') - return can, None if can else "Vous ne pouvez pas voir cette application." + return can, None if can else _("You don't have the rights to see this application.") diff --git a/cotisations/forms.py b/cotisations/forms.py index 3459f0de..696f1f5c 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -20,19 +20,18 @@ # 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. +Forms for the 'cotisation' app of re2o. It highly depends on +:cotisations:models and is mainly used by :cotisations: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) +The following forms are mainly used to create, edit or delete +anything related to 'cotisations' : + * Payments Methods + * Banks + * Invoices + * Articles -ArticleForm, BanqueForm, PaiementForm permettent aux admin d'ajouter, -éditer ou supprimer une banque/moyen de paiement ou un article +See the details for each of these operations in the documentation +of each of the method. """ from __future__ import unicode_literals @@ -40,6 +39,9 @@ from django import forms from django.db.models import Q from django.forms import ModelForm, Form from django.core.validators import MinValueValidator,MaxValueValidator +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _l + from .models import Article, Paiement, Facture, Banque from preferences.models import OptionalUser from users.models import User @@ -48,17 +50,21 @@ from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin class NewFactureForm(FormRevMixin, ModelForm): - """Creation d'une facture, moyen de paiement, banque et numero - de cheque""" + """ + Form used to create a new invoice by using a payment method, a bank and a + cheque number. + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(NewFactureForm, self).__init__(*args, prefix=prefix, **kwargs) + # TODO : remove the use of cheque and banque and paiement + # for something more generic or at least in English 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\ - un moyen de paiement" + self.fields['cheque'].label = _("Cheque number") + self.fields['banque'].empty_label = _("Not specified") + self.fields['paiement'].empty_label = \ + _("Select a payment method") paiement_list = Paiement.objects.filter(type_paiement=1) if paiement_list: self.fields['paiement'].widget\ @@ -70,96 +76,119 @@ class NewFactureForm(FormRevMixin, ModelForm): def clean(self): cleaned_data = super(NewFactureForm, self).clean() - paiement = cleaned_data.get("paiement") - cheque = cleaned_data.get("cheque") - banque = cleaned_data.get("banque") + 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.") + raise forms.ValidationError( + _("A payment method must be specified.") + ) + elif paiement.type_paiement == 'check' and not (cheque and banque): + raise forms.ValidationError( + _("A cheque number and a bank must be specified.") + ) return cleaned_data class CreditSoldeForm(NewFactureForm): - """Permet de faire des opérations sur le solde si il est activé""" + """ + Form used to make some operations on the user's balance if the option is + activated. + """ class Meta(NewFactureForm.Meta): model = Facture fields = ['paiement', 'banque', 'cheque'] def __init__(self, *args, **kwargs): super(CreditSoldeForm, self).__init__(*args, **kwargs) + # TODO : change solde to balance self.fields['paiement'].queryset = Paiement.objects.exclude( moyen='solde' - ).exclude(moyen="Solde") + ).exclude(moyen='Solde') montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True) class SelectUserArticleForm(FormRevMixin, Form): - """Selection d'un article lors de la creation d'une facture""" + """ + Form used to select an article during the creation of an invoice for a member. + """ article = forms.ModelChoiceField( queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Adherent')), - label="Article", + label=_l("Article"), required=True ) quantity = forms.IntegerField( - label="Quantité", + label=_l("Quantity"), validators=[MinValueValidator(1)], required=True ) class SelectClubArticleForm(Form): - """Selection d'un article lors de la creation d'une facture""" + """ + Form used to select an article during the creation of an invoice for a club. + """ article = forms.ModelChoiceField( queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Club')), - label="Article", + label=_l("Article"), required=True ) quantity = forms.IntegerField( - label="Quantité", + label=_l("Quantity"), validators=[MinValueValidator(1)], required=True ) - +# TODO : change Facture to Invoice class NewFactureFormPdf(Form): - """Creation d'un pdf facture par le trésorier""" + """ + Form used to create a custom PDF invoice. + """ article = forms.ModelMultipleChoiceField( queryset=Article.objects.all(), - label="Article" + label=_l("Article") ) number = forms.IntegerField( - label="Quantité", + label=_l("Quantity"), validators=[MinValueValidator(1)] ) - paid = forms.BooleanField(label="Payé", required=False) - dest = forms.CharField(required=True, max_length=255, label="Destinataire") - chambre = forms.CharField(required=False, max_length=10, label="Adresse") + paid = forms.BooleanField(label=_l("Paid"), required=False) + # TODO : change dest field to recipient + dest = forms.CharField(required=True, max_length=255, label=_l("Recipient")) + # TODO : change chambre field to address + chambre = forms.CharField(required=False, max_length=10, label=_l("Address")) + # TODO : change fid field to invoice_id fid = forms.CharField( required=True, max_length=10, - label="Numéro de la facture" + label=_l("Invoice number") ) - +# TODO : change Facture to Invoice class EditFactureForm(FieldPermissionFormMixin, NewFactureForm): - """Edition d'une facture : moyen de paiement, banque, user parent""" + """ + Form used to edit an invoice and its fields : payment method, bank, + user associated, ... + """ class Meta(NewFactureForm.Meta): + # TODO : change Facture to Invoice model = Facture fields = '__all__' def __init__(self, *args, **kwargs): + # TODO : change Facture to Invoice super(EditFactureForm, self).__init__(*args, **kwargs) - self.fields['user'].label = 'Adherent' - self.fields['user'].empty_label = "Séléctionner\ - l'adhérent propriétaire" - self.fields['valid'].label = 'Validité de la facture' + self.fields['user'].label = _("Member") + self.fields['user'].empty_label = \ + _("Select the proprietary member") + self.fields['valid'].label = _("Validated invoice") class ArticleForm(FormRevMixin, ModelForm): - """Creation d'un article. Champs : nom, cotisation, durée""" + """ + Form used to create an article. + """ class Meta: model = Article fields = '__all__' @@ -167,15 +196,17 @@ class ArticleForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = "Désignation de l'article" + self.fields['name'].label = _("Article name") class DelArticleForm(FormRevMixin, Form): - """Suppression d'un ou plusieurs articles en vente. Choix - parmis les modèles""" + """ + Form used to delete one or more of the currently available articles. + The user must choose the one to delete by checking the boxes. + """ articles = forms.ModelMultipleChoiceField( queryset=Article.objects.none(), - label="Articles actuels", + label=_l("Existing articles"), widget=forms.CheckboxSelectMultiple ) @@ -188,26 +219,39 @@ class DelArticleForm(FormRevMixin, Form): self.fields['articles'].queryset = Article.objects.all() +# TODO : change Paiement to Payment class PaiementForm(FormRevMixin, ModelForm): - """Creation d'un moyen de paiement, champ text moyen et type - permettant d'indiquer si il s'agit d'un chèque ou non pour le form""" + """ + Form used to create a new payment method. + The 'cheque' type is used to associate a specific behaviour requiring + a cheque number and a bank. + """ class Meta: model = Paiement + # TODO : change moyen to method and type_paiement to payment_type fields = ['moyen', 'type_paiement'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['moyen'].label = 'Moyen de paiement à ajouter' - self.fields['type_paiement'].label = 'Type de paiement à ajouter' + self.fields['moyen'].label = _("Payment method name") + self.fields['type_paiement'].label = _("Payment type") + self.fields['type_paiement'].help_text = \ + _("The payement type is used for specific behaviour.\ + The \"cheque\" type means a cheque number and a bank name\ + may be added when using this payment method.") +# TODO : change paiement to payment class DelPaiementForm(FormRevMixin, Form): - """Suppression d'un ou plusieurs moyens de paiements, selection - parmis les models""" + """ + Form used to delete one or more payment methods. + The user must choose the one to delete by checking the boxes. + """ + # TODO : change paiement to payment paiements = forms.ModelMultipleChoiceField( queryset=Paiement.objects.none(), - label="Moyens de paiement actuels", + label=_l("Existing payment method"), widget=forms.CheckboxSelectMultiple ) @@ -220,23 +264,32 @@ class DelPaiementForm(FormRevMixin, Form): self.fields['paiements'].queryset = Paiement.objects.all() +# TODO : change banque to bank class BanqueForm(FormRevMixin, ModelForm): - """Creation d'une banque, field name""" + """ + Form used to create a bank. + """ class Meta: + # TODO : change banque to bank model = Banque fields = ['name'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(BanqueForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['name'].label = 'Banque à ajouter' + self.fields['name'].label = _("Bank name") +# TODO : change banque to bank class DelBanqueForm(FormRevMixin, Form): - """Selection d'une ou plusieurs banques, pour suppression""" + """ + Form used to delete one or more banks. + The use must choose the one to delete by checking the boxes. + """ + # TODO : change banque to bank banques = forms.ModelMultipleChoiceField( queryset=Banque.objects.none(), - label="Banques actuelles", + label=_l("Existing banks"), widget=forms.CheckboxSelectMultiple ) @@ -249,43 +302,59 @@ class DelBanqueForm(FormRevMixin, Form): self.fields['banques'].queryset = Banque.objects.all() +# TODO : change facture to Invoice class NewFactureSoldeForm(NewFactureForm): - """Creation d'une facture, moyen de paiement, banque et numero - de cheque""" + """ + Form used to create an invoice + """ 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" + self.fields['cheque'].label = _('Cheque number') + self.fields['banque'].empty_label = _("Not specified") + self.fields['paiement'].empty_label = \ + _("Select a payment method") + # TODO : change paiement to payment paiement_list = Paiement.objects.filter(type_paiement=1) if paiement_list: self.fields['paiement'].widget\ .attrs['data-cheque'] = paiement_list.first().id class Meta: + # TODO : change facture to invoice model = Facture + # TODO : change paiement to payment and baque to bank fields = ['paiement', 'banque'] def clean(self): cleaned_data = super(NewFactureSoldeForm, self).clean() + # TODO : change paiement to payment paiement = cleaned_data.get("paiement") cheque = cleaned_data.get("cheque") + # TODO : change banque to bank banque = cleaned_data.get("banque") + # TODO : change paiement to payment if not paiement: - raise forms.ValidationError("Le moyen de paiement est obligatoire") + raise forms.ValidationError( + _("A payment method must be specified.") + ) + # TODO : change paiement and banque to payment and bank elif paiement.type_paiement == "check" and not (cheque and banque): - raise forms.ValidationError("Le numéro de chèque et\ - la banque sont obligatoires.") + raise forms.ValidationError( + _("A cheque number and a bank must be specified.") + ) return cleaned_data +# TODO : Better name and docstring class RechargeForm(FormRevMixin, Form): + """ + Form used to refill a user's balance + """ value = forms.FloatField( - label='Valeur', + label=_l("Amount"), min_value=0.01, validators = [] ) @@ -297,7 +366,21 @@ class RechargeForm(FormRevMixin, Form): def clean_value(self): value = self.cleaned_data['value'] if value < OptionalUser.get_cached_value('min_online_payment'): - raise forms.ValidationError("Montant inférieur au montant minimal de paiement en ligne (%s) €" % OptionalUser.get_cached_value('min_online_payment')) + raise forms.ValidationError( + _("Requested amount is too small. Minimum amount possible : \ + %(min_online_amount)s €.") % { + min_online_amount: OptionalUser.get_cached_value( + 'min_online_payment' + ) + } + ) if value + self.user.solde > OptionalUser.get_cached_value('max_solde'): - raise forms.ValidationError("Le solde ne peux excéder %s " % OptionalUser.get_cached_value('max_solde')) + raise forms.ValidationError( + _("Requested amount is too high. Your balance can't exceed \ + %(max_online_balance)s €.") % { + max_online_balance: OptionalUser.get_cached_value( + 'max_solde' + ) + } + ) return value diff --git a/cotisations/locale/fr/LC_MESSAGES/django.mo b/cotisations/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 00000000..421bfad2 Binary files /dev/null and b/cotisations/locale/fr/LC_MESSAGES/django.mo differ diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..c2323c45 --- /dev/null +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,825 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 Maël Kervella +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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. +msgid "" +msgstr "" +"Project-Id-Version: 2.5\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-31 14:05+0000\n" +"PO-Revision-Date: 2018-03-31 16:09+0002\n" +"Last-Translator: Maël Kervella \n" +"Language-Team: \n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: acl.py:41 +msgid "You don't have the rights to see this application." +msgstr "Vous n'avez pas les droits de voir cette application." + +#: forms.py:63 cotisations/forms.py:298 cotisations/models.py:86 +msgid "Cheque number" +msgstr "Numéro de chèque" + +#: forms.py:64 cotisations/forms.py:299 +msgid "Not specified" +msgstr "Non renseigné" + +#: forms.py:66 cotisations/forms.py:301 +msgid "Select a payment method" +msgstr "Sélectionnez un moyen de paiement" + +#: forms.py:83, cotisations/forms.py:325 +msgid "A payment method must be specified." +msgstr "Un moyen de paiement doit être renseigné." + +#: forms.py:87, cotisations/forms.py:330 +msgid "A cheque number and a bank must be specified." +msgstr "Un numéro de chèqe et une banque doivent être renseignés." + +#: forms.py:114 cotisations/forms.py:129 cotisations/forms.py:144 +#: models.py:243 +#: templates/cotisations/aff_article.html:31 +#: templates/cotisations/new_facture.html:50 +#: templates/cotisations/new_facture_solde.html:44 +msgid "Article" +msgstr "Article" + +#: forms.py:118 cotisations/forms.py:133 cotisations/forms.py:147 +#: templates/cotisations/edit_facture.html:46 +msgid "Quantity" +msgstr "Quantité" + +#: forms.py:150 +msgid "Paid" +msgstr "Payé" + +#: forms.py:152 +msgid "Recipient" +msgstr "Destinataire" + +#: forms.py:154 +msgid "Address" +msgstr "Adresse" + +#: forms.py:159 +msgid "Invoice number" +msgstr "Numéro de facture" + +#: forms.py:174 cotisations/models.py:403 +msgid "Member" +msgstr "Adhérent" + +#: forms.py:176 +msgid "Select the proprietary member" +msgstr "Sélectionnez l'adhérent propriétaire" + +#: forms.py:177 +msgid "Validated invoice" +msgstr "Facture validée" + +#: forms.py:190 +msgid "Article name" +msgstr "Nom de l'article" + +#: forms.py:199 +msgid "Existing articles" +msgstr "Articles disponibles" + +#: forms.py:225 +msgid "Payment method name" +msgstr "Nom du moyen de paiement" + +#: forms.py:226 cotisations/models.py:505 +msgid "Payment type" +msgstr "Type de paiement" + +#: forms.py:228 +msgid "" +"The payement type is used for specific behaviour. The \"cheque\" " +"type means a cheque number and a bank name may be added when " +"using this payment method." +msgstr "" +"Le type de paiement est utilisé pour des comportements spécifiques. Le type " +"\"chèque\" permet de spécifier un numéro de chèque et une banque lors de " +" l'utilisation de cette méthode." + +#: forms.py:241 +msgid "Existing payment method" +msgstr "Moyen de paiements disponibles" + +#: forms.py:266 +msgid "Bank name" +msgstr "Nom de la banque" + +#: forms.py:276 +msgid "Existing banks" +msgstr "Banques disponibles" + +#: forms.py:337 cotisations/models.py:238 +msgid "Amount" +msgstr "Montant" + +#: forms.py:350 +#, python-format +msgid "" +"Requested amount is too small. Minimum amount possible : " +"%(min_online_amount)s €." +msgstr "" +"Montant demandé est trop faible. Montant minimal possible : " +"%(min_online_amount)s €" + +#: forms.py:359 +#, python-format +msgid "" +"Requested amount is too high. Your balance can't exceed " +"%(max_online_balance)s €." +msgstr "" +"Montant demandé trop grand. Votre solde ne peut excéder " +"%(max_online_balance)s €" + +#: models.py:90 +#: templates/cotisations/aff_cotisations.html:47 +#: templates/cotisations/control.html:67 +msgid "Date" +msgstr "Date" + +#: models.py:95 +#: templates/cotisations/control.html:71 +msgid "Validated" +msgstr "Validé" + +#: models.py:100 +#: templates/cotisations/control.html:75 +msgid "Controlled" +msgstr "Controllé" + +#: models.py:107 +msgid "Can change the \"controlled\" state" +msgstr "Peut modifier l'état \"controllé\"" + +#: models.py:109 +msgid "Can create a custom PDF invoice" +msgstr "Peut crée une facture PDF personnalisée" + +#: models.py:110 +msgid "Can see an invoice's details" +msgstr "Peut voir les détails d'une facture" + +#: models.py:111 +msgid "Can edit all the previous invoices" +msgstr "Peut modifier toutes les factures existantes" + +#: models.py:113 cotisations/models.py:233 +msgid "Invoice" +msgstr "Facture" + +#: models.py:114 +#: templates/cotisations/index.html:29 templates/cotisations/sidebar.html:40 +msgid "Invoices" +msgstr "Factures" + +#: models.py:152 cotisations/models.py:186 +msgid "You don't have the right to edit an invoice." +msgstr "Vous n'avez pas le droit de modifier une facture." + +#: models.py:154 +msgid "You don't have the right to edit this user's invoices." +msgstr "Vous n'avez pas le droit de modifier les facture de cette utilisateur." + +#: models.py:157 +msgid "" +"You don't have the right to edit an invoice already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de modifier une facture précedement controllée " +"ou invalidée." + +#: models.py:163 +msgid "You don't have the right to delete an invoice." +msgstr "Vous n'avez pas le droit de supprimer une facture." + +#: models.py:165 +msgid "You don't have the right to delete this user's invoices." +msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur." + +#: models.py:167 +msgid "" +"You don't have the right to delete an invoice already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de supprimer une facture précedement controllée " +"ou invalidée." + +#: models.py:174 +msgid "You don't have the right to see someone else's invoices history." +msgstr "" +"Vous n'avez pas le droit de voir l'historique de la facture de " +"quelqu'un d'autre." + +#: models.py:176 +msgid "The invoice has been invalidated." +msgstr "La facture a été invalidée." + +#: models.py:182 +msgid "You don't have the right to edit the controlled state." +msgstr "Vous n'avez pas le droit de modifier l'état \"controllé\"." + +#: models.py:224 cotisations/models.py:409 +#: models.py:536 +msgid "Connexion" +msgstr "Connexion" + +#: models.py:225 cotisations/models.py:410 +msgid "Membership" +msgstr "Adhésion" + +#: models.py:226 cotisations/models.py:405 +#: models.py:411 cotisations/models.py:538 +msgid "Both of them" +msgstr "Les deux" + +#: models.py:250 +#: templates/cotisations/aff_article.html:32 +msgid "Price" +msgstr "Prix" + +#: models.py:255 cotisations/models.py:428 +msgid "Duration (in whole month)" +msgstr "Durée (en mois entiers)" + +#: models.py:263 cotisations/models.py:442 +#: models.py:552 +msgid "Type of cotisation" +msgstr "Type de cotisation" + +#: models.py:268 +msgid "Can see a purchase's details" +msgstr "Peut voir les détails d'un achat" + +#: models.py:269 +msgid "Can edit all the previous purchases" +msgstr "Peut voir les achats existants" + +#: models.py:271 cotisations/models.py:546 +msgid "Purchase" +msgstr "Achat" + +#: models.py:272 +msgid "Purchases" +msgstr "Achat" + +#: models.py:328 +msgid "A cotisation should always have a duration." +msgstr "Une cotisation devrait toujours avoir une durée." + +#: models.py:335 +msgid "You don't have the right to edit the purchases." +msgstr "Vous n'avez pas le droit de modifier les achats." + +#: models.py:337 +msgid "You don't have the right to edit this user's purchases." +msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." + +#: models.py:340 +msgid "" +"You don't have the right to edit a purchase already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de modifier un achat précédement controllé ou " +"invalidé." + +#: models.py:346 +msgid "You don't have the right to delete a purchase." +msgstr "Vous n'avez pas le droit de supprimer un achat." + +#: models.py:348 +msgid "You don't have the right to delete this user's purchases." +msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." + +#: models.py:350 +msgid "" +"You don't have the right to delete a purchase already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de supprimer un achat précédement controllé ou " +"invalidé." + +#: models.py:357 +msgid "You don't have the right to see someone else's purchase history." +msgstr "Vous n'avez pas le droit de voir l'historique d'un achat de quelqu'un d'autre." + +#: models.py:404 +msgid "Club" +msgstr "Club" + +#: models.py:416 +#: templates/cotisations/aff_cotisations.html:40 +#: templates/cotisations/control.html:60 +#: templates/cotisations/edit_facture.html:45 +msgid "Designation" +msgstr "Désignation" + +#: models.py:422 +msgid "Unitary price" +msgstr "Prix unitaire" + +#: models.py:434 +msgid "Type of users concerned" +msgstr "Type d'utilisateurs concernés" + +#: models.py:449 +msgid "Can see an article's details" +msgstr "Peut voir les détails d'un article" + +#: models.py:457 +msgid "Solde is a reserved article name" +msgstr "Solde est un nom d'article réservé" + +#: models.py:461 +msgid "Duration must be specified for a cotisation" +msgstr "La durée doit être spécifiée pour une cotisation" + +#: models.py:474 +msgid "Name" +msgstr "Nom" + +#: models.py:479 +msgid "Can see a bank's details" +msgstr "Peut voir les détails d'une banque" + +#: models.py:481 +#: templates/cotisations/aff_banque.html:31 +msgid "Bank" +msgstr "Banque" + +#: models.py:482 +#: templates/cotisations/index_banque.html:30 +#: templates/cotisations/sidebar.html:50 +msgid "Banks" +msgstr "Banques" + +#: models.py:493 +msgid "Standard" +msgstr "Standard" + +#: models.py:494 +msgid "Cheque" +msgstr "Chèque" + +#: models.py:500 +msgid "Method" +msgstr "Moyen" + +#: models.py:510 +msgid "Can see a payement's details" +msgstr "Peut voir les détails d'un paiement" + +#: models.py:512 +#: templates/cotisations/aff_cotisations.html:43 +#: templates/cotisations/aff_paiement.html:31 +#: templates/cotisations/control.html:63 +msgid "Payment method" +msgstr "Moyen de paiement" + +#: models.py:513 +#: templates/cotisations/sidebar.html:55 +msgid "Payment methods" +msgstr "Moyens de paiement" + +#: models.py:526 +msgid "You cannot have multiple payment method of type cheque" +msgstr "Vous ne pouvez avoir plusieurs moyens de paiement de type chèque" + +#: models.py:555 +msgid "Starting date" +msgstr "Date de début" + +#: models.py:558 +msgid "Ending date" +msgstr "Date de fin" + +#: models.py:563 +msgid "Can see a cotisation's details" +msgstr "Peut voir les détails d'une cotisation" + +#: models.py:564 +msgid "Can edit the previous cotisations" +msgstr "Peut voir les cotisations existantes" + +#: models.py:569 +msgid "You don't have the right to edit a cotisation." +msgstr "Vous n'avez pas le droit de modifier une cotisation." + +#: models.py:572 +msgid "" +"You don't have the right to edit a cotisation already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de modifier une cotisaiton précédement controllée " +"ou invalidée." + +#: models.py:578 +msgid "You don't have the right to delete a cotisation." +msgstr "Vous n'avez pas le droit de supprimer une cotisation." + +#: models.py:580 +msgid "" +"You don't have the right to delete a cotisation already controlled or " +"invalidated." +msgstr "" +"Vous n'avez pas le droit de supprimer une cotisation précédement controllée " +"ou invalidée." + +#: models.py:587 +msgid "You don't have the right to see someone else's cotisation history." +msgstr "" +"Vous n'avez pas le droit de voir l'historique d'une cotisation de " +"quelqu'un d'autre." + +#: payment.py:26 +#, python-format +msgid "The payment of %(amount)s € has been accepted." +msgstr "Le paiement de %(amount)s € a été accepté." + +#: payment.py:38 +msgid "The payment has been refused." +msgstr "Le paiment a été refusé." + +#: templates/cotisations/aff_article.html:33 +msgid "Cotisation type" +msgstr "Type de cotisation" + +#: templates/cotisations/aff_article.html:34 +msgid "Duration (month)" +msgstr "Durée (mois)" + +#: templates/cotisations/aff_article.html:35 +msgid "Concerned users" +msgstr "Utilisateurs concernés" + +#: templates/cotisations/aff_article.html:48 +#: templates/cotisations/aff_banque.html:40 +#: templates/cotisations/aff_cotisations.html:69 +#: templates/cotisations/aff_cotisations.html:75 +#: templates/cotisations/aff_paiement.html:40 +#: templates/cotisations/control.html:104 +msgid "Edit" +msgstr "Modifier" + +#: templates/cotisations/aff_article.html:52 +#: templates/cotisations/aff_banque.html:44 +#: templates/cotisations/aff_cotisations.html:90 +#: templates/cotisations/aff_paiement.html:44 +msgid "Historique" +msgstr "Historique" + +#: templates/cotisations/aff_cotisations.html:37 +msgid "User" +msgstr "Utilisateur" + +#: templates/cotisations/aff_cotisations.html:41 +#: templates/cotisations/control.html:61 +msgid "Total price" +msgstr "Prix total" + +#: templates/cotisations/aff_cotisations.html:51 +#: templates/cotisations/control.html:53 +msgid "Invoice id" +msgstr "Id facture" + +#: templates/cotisations/aff_cotisations.html:79 +msgid "Controlled invoice" +msgstr "Facture controllé" + +#: templates/cotisations/aff_cotisations.html:84 +msgid "Delete" +msgstr "Supprimer" + +#: templates/cotisations/aff_cotisations.html:99 +msgid "PDF" +msgstr "PDF" + +#: templates/cotisations/aff_cotisations.html:102 +msgid "Invalidated invoice" +msgstr "Facture invalidée" + +#: templates/cotisations/control.html:30 +msgid "Invoice control" +msgstr "Contrôle des factures" + +#: templates/cotisations/control.html:33 +msgid "Invoice control and validation" +msgstr "Contrôle et validation des factures" + +#: templates/cotisations/control.html:43 +msgid "Profil" +msgstr "Profil" + +#: templates/cotisations/control.html:45 +msgid "Last name" +msgstr "Nom" + +#: templates/cotisations/control.html:49 +msgid "First name" +msgstr "Prénom" + +#: templates/cotisations/control.html:57 +msgid "User id" +msgstr "Id utilisateur" + +#: templates/cotisations/delete.html:29 +msgid "Deletion of cotisations" +msgstr "Supprimer des cotisations" + +#: templates/cotisations/delete.html:36 +#, python-format +msgid "" +"\n" +" Warning. Are you sure you really want te delete this %(object_name)s " +"object ( %(objet)s ) ?\n" +" " +msgstr "" +"\n" +" Attention. Êtes-vous vraiment sûr de vouloir supprimer cet objet " +"%(object_name)s ( %(objet)s ) ?\n" +" " + +#: templates/cotisations/delete.html:40 +#: templates/cotisations/edit_facture.html:60 +#: templates/cotisations/new_facture_solde.html:59 +#: templates/cotisations/recharge.html:42 +msgid "Confirm" +msgstr "Confirmer" + +#: templates/cotisations/edit_facture.html:31 +#: templates/cotisations/facture.html:30 +#: templates/cotisations/new_facture.html:30 +#: templates/cotisations/new_facture_solde.html:30 +msgid "Invoices creation and edition" +msgstr "Création et modification de factures" + +#: templates/cotisations/edit_facture.html:38 +msgid "Edit the invoice" +msgstr "Edition de factures" + +#: templates/cotisations/edit_facture.html:41 +#: templates/cotisations/new_facture.html:46 +#: templates/cotisations/new_facture_solde.html:40 +msgid "Invoice's articles" +msgstr "Articles de la facture" + +#: templates/cotisations/index.html:32 +msgid "Cotisations" +msgstr "Cotisations" + +#: templates/cotisations/index_article.html:30 +msgid "Articles" +msgstr "Articles" + +#: templates/cotisations/index_article.html:33 +msgid "Article types list" +msgstr "Liste des types d'articles" + +#: templates/cotisations/index_article.html:36 +msgid "Add an article type" +msgstr "Ajouter un type d'article" + +#: templates/cotisations/index_article.html:40 +msgid "Delete article types" +msgstr "Supprimer des types d'articles" + +#: templates/cotisations/index_banque.html:33 +msgid "Banks list" +msgstr "Liste des banques" + +#: templates/cotisations/index_banque.html:36 +msgid "Add a bank" +msgstr "Ajouter une banque" + +#: templates/cotisations/index_banque.html:40 +msgid "Delete banks" +msgstr "Supprimer des banques" + +#: templates/cotisations/index_paiement.html:30 +msgid "Payments" +msgstr "Paiement" + +#: templates/cotisations/index_paiement.html:33 +msgid "Payment types list" +msgstr "Liste des types de paiement" + +#: templates/cotisations/index_paiement.html:36 +msgid "Add a payment type" +msgstr "Ajouter un type de paiement" + +#: templates/cotisations/index_paiement.html:40 +msgid "Delete payment types" +msgstr "Supprimer un type de paiement" + +#: templates/cotisations/new_facture.html:37 +#: templates/cotisations/new_facture_solde.html:37 +msgid "New invoice" +msgstr "Nouvelle facture" + +#: templates/cotisations/new_facture.html:39 +#, python-format +msgid "" +"\n" +" User's balance : %(user.solde)s €\n" +" " +msgstr "" +"\n" +" Solde de l'utilisateur : %(user.solde)s €\n" +" " + +#: templates/cotisations/new_facture.html:59 +#: templates/cotisations/new_facture_solde.html:53 +msgid "Add an article" +msgstr "Ajouter un article" + +#: templates/cotisations/new_facture.html:61 +#: templates/cotisations/new_facture_solde.html:55 +msgid "" +"\n" +" Total price : 0,00 €\n" +" " +msgstr "" +"\n" +" Prix total : 0,00 €\n" +" " + +#: templates/cotisations/new_facture.html:65 +msgid "Create" +msgstr "Créer" + +#: templates/cotisations/payment.html:30 templates/cotisations/recharge.html:30 +#: templates/cotisations/recharge.html:33 +msgid "Balance refill" +msgstr "Rechargement de solde" + +#: templates/cotisations/payment.html:34 +#, python-format +msgid "" +"\n" +" Refill of %(amount)s €\n" +" " +msgstr "" +"\n" +" Recharger de %(amount)s €\n" +" " + +#: templates/cotisations/payment.html:40 +msgid "Pay" +msgstr "Payer" + +#: templates/cotisations/recharge.html:35 +#, python-format +msgid "" +"\n" +" Balance : %(request.user.solde)s " +"€\n" +" " +msgstr "" +"\n" +" Solde : %(request.user.solde)s " +"€\n" +" " + +#: templates/cotisations/sidebar.html:32 +msgid "Create an invoice" +msgstr "Créer une facture" + +#: templates/cotisations/sidebar.html:35 +msgid "Control the invoices" +msgstr "Contrôler les factures" + +#: templates/cotisations/sidebar.html:45 +msgid "Available articles" +msgstr "Articles disponibles" + +#: views.py:133 +msgid "Your balance is too low for this operation." +msgstr "Votre solde est trop faible pour cette opération." + +#: views.py:163 +#, python-format +msgid "" +"The cotisation of %(member_name)s has been extended to " +"%(end_date)s." +msgstr "La cotisation de %(member_name)s a été étendu jusqu'à %(end_date)s." + +#: views.py:172 +msgid "The invoice has been created." +msgstr "La facture a été créée." + +#: views.py:180 cotisations/views.py:777 +msgid "You need to choose at least one article." +msgstr "Vous devez choisir au moins un article." + +#: views.py:292 +msgid "The invoice has been successfully edited." +msgstr "La facture a été crée avec succès." + +#: views.py:314 +msgid "The invoice has been successfully deleted." +msgstr "La facture a été supprimée avec succès." + +#: views.py:351 +msgid "Balance successfully updated." +msgstr "Solde mis à jour avec succès." + +#: views.py:376 +msgid "The article has been successfully created." +msgstr "L'article a été créé avec succès." + +#: views.py:400 +msgid "The article has been successfully edited." +msgstr "L'article a été modifié avec succès." + +#: views.py:419 +msgid "The article(s) have been successfully deleted." +msgstr "L'(es) article(s) a(ont) été supprimé(s) avec succès. " + +#: views.py:441 +msgid "The payment method has been successfully created." +msgstr "Le moyen de paiement a été créé avec succès." + +#: views.py:465 +msgid "The payement method has been successfully edited." +msgstr "Le moyen de paiement a été modifié avec succès." + +#: views.py:488 +#, python-format +msgid "" +"The payment method %(method_name)s has been successfully " +"deleted." +msgstr "Le moyen de paiement %(method_name)s a été supprimé avec succès." + +#: views.py:496 +#, python-format +msgid "" +"The payment method %(method_name)s can't be deleted " +"because there are invoices using it." +msgstr "" +"Le moyen de paiement %(method_name)s ne peut pas être mis à jour car il y a " +"des factures l'utilisant." + +#: views.py:519 +msgid "The bank has been successfully created." +msgstr "La banque a été crée avec succès." + +#: views.py:543 +msgid "The bank has been successfully edited" +msgstr "La banque a été modifée avec succès." + +#: views.py:566 +#, python-format +msgid "" +"The bank %(bank_name)s has been successfully deleted." +msgstr "La banque %(bank_name)s a été supprimée avec succès." + +#: views.py:574 +#, python-format +msgid "" +"The bank %(bank_name)s can't be deleted because there " +"are invoices using it." +msgstr "" +"La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " +"qui l'utilisent." + +#: views.py:730 +msgid "The balance is too low for this operation." +msgstr "Le solde est trop faible pour cette opération." + +#: views.py:760 +#, python-format +msgid "" +"The cotisation of %(member_name)s has been successfully " +"extended to %(end_date)s." +msgstr "La cotisation de %(member_name)s a été prolongée jusqu'à %(end_date)s." + +#: views.py:769 +msgid "The invoice has been successuflly created." +msgstr "La facture a été créée avec succès." + +#: views.py:796 +msgid "Online payment is disabled." +msgstr "Le paiement en ligne est désactivé." diff --git a/cotisations/models.py b/cotisations/models.py index cdf73a39..3edc7815 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -21,26 +21,13 @@ # 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. +The database models for the 'cotisation' app of re2o. +The goal is to keep the main actions here, i.e. the 'clean' and 'save' +function are higly reposnsible for the changes, checking the coherence of the +data and the good behaviour in general for not breaking the database. -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 +For further details on each of those models, see the documentation details for +each. """ from __future__ import unicode_literals @@ -55,55 +42,101 @@ from django.core.validators import MinValueValidator from django.db.models import Max from django.utils import timezone from machines.models import regen +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _l from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin +# TODO : change facture to invoice class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): - """ Définition du modèle des factures. Une facture regroupe une ou - plusieurs ventes, rattachée à un user, et reliée à un moyen de paiement - et si il y a lieu un numero pour les chèques. Possède les valeurs - valides et controle (trésorerie)""" - PRETTY_NAME = "Factures émises" + """ + The model for an invoice. It reprensents the fact that a user paid for + something (it can be multiple article paid at once). + + An invoice is linked to : + * one or more purchases (one for each article sold that time) + * a user (the one who bought those articles) + * a payment method (the one used by the user) + * (if applicable) a bank + * (if applicable) a cheque number. + Every invoice is dated throught the 'date' value. + An invoice has a 'controlled' value (default : False) which means that + someone with high enough rights has controlled that invoice and taken it + into account. It also has a 'valid' value (default : True) which means + that someone with high enough rights has decided that this invoice was not + valid (thus it's like the user never paid for his articles). It may be + necessary in case of non-payment. + """ user = models.ForeignKey('users.User', on_delete=models.PROTECT) + # TODO : change paiement to payment paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) + # TODO : change banque to bank banque = models.ForeignKey( '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) + null=True + ) + # TODO : maybe change to cheque nummber because not evident + cheque = models.CharField( + max_length=255, + blank=True, + verbose_name=_l("Cheque number") + ) + date = models.DateTimeField( + auto_now_add=True, + verbose_name=_l("Date") + ) + # TODO : change name to validity for clarity + valid = models.BooleanField( + default=True, + verbose_name=_l("Validated") + ) + # TODO : changed name to controlled for clarity + control = models.BooleanField( + default=False, + verbose_name=_l("Controlled") + ) class Meta: abstract = False permissions = ( - ("change_facture_control", "Peut changer l'etat de controle"), - ("change_facture_pdf", "Peut éditer une facture pdf"), - ("view_facture", "Peut voir un objet facture"), - ("change_all_facture", "Superdroit, peut modifier toutes les factures"), + # TODO : change facture to invoice + ('change_facture_control', _l("Can change the \"controlled\" state")), + # TODO : seems more likely to be call create_facture_pdf or create_invoice_pdf + ('change_facture_pdf', _l("Can create a custom PDF invoice")), + ('view_facture', _l("Can see an invoice's details")), + ('change_all_facture', _l("Can edit all the previous invoices")), ) + verbose_name = _l("Invoice") + verbose_name_plural = _l("Invoices") def linked_objects(self): """Return linked objects : machine and domain. Usefull in history display""" return self.vente_set.all() + # TODO : change prix to price def prix(self): - """Renvoie le prix brut sans les quantités. Méthode - dépréciée""" - prix = Vente.objects.filter( + """ + Returns: the raw price without the quantities. + Deprecated, use :total_price instead. + """ + price = Vente.objects.filter( facture=self ).aggregate(models.Sum('prix'))['prix__sum'] - return prix + return price + # TODO : change prix to price def prix_total(self): - """Prix total : somme des produits prix_unitaire et quantité des - ventes de l'objet""" + """ + Returns: the total price for an invoice. Sum all the articles' prices + and take the quantities into account. + """ + # TODO : change Vente to somethingelse return Vente.objects.filter( facture=self ).aggregate( @@ -114,7 +147,10 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): )['total'] def name(self): - """String, somme des name des ventes de self""" + """ + Returns : a string with the name of all the articles in the invoice. + Used for reprensenting the invoice with a string. + """ name = ' - '.join(Vente.objects.filter( facture=self ).values_list('name', flat=True)) @@ -122,44 +158,41 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_facture'): - return False, u"Vous n'avez pas le droit d'éditer les factures" + return False, _("You don't have the right to edit an invoice.") elif not user_request.has_perm('cotisations.change_all_facture') and not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" + return False, _("You don't have the right to edit this user's invoices.") elif not user_request.has_perm('cotisations.change_all_facture') and\ (self.control or not self.valid): - return False, u"Vous n'avez pas le droit d'éditer une facture\ - controlée ou invalidée par un trésorier" + return False, _("You don't have the right to edit an invoice already controlled or invalidated.") else: return True, None def can_delete(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.delete_facture'): - return False, u"Vous n'avez pas le droit de supprimer une facture" + return False, _("You don't have the right to delete an invoice.") if not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" + return False, _("You don't have the right to delete this user's invoices.") if self.control or not self.valid: - return False, u"Vous ne pouvez pas supprimer une facture\ - contrôlée ou invalidée par un trésorier" + return False, _("You don't have the right to delete an invoice already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.view_facture') and\ self.user != user_request: - return False, u"Vous ne pouvez pas afficher l'historique d'une\ - facture d'un autre user que vous sans droit cableur" + return False, _("You don't have the right to see someone else's invoices history.") elif not self.valid: - return False, u"La facture est invalidée et ne peut être affichée" + return False, _("The invoice has been invalidated.") else: return True, None @staticmethod def can_change_control(user_request, *args, **kwargs): - return user_request.has_perm('cotisations.change_facture_control'), "Vous ne pouvez pas éditer le controle sans droit trésorier" + return user_request.has_perm('cotisations.change_facture_control'), _("You don't have the right to edit the \"controlled\" state.") @staticmethod def can_change_pdf(user_request, *args, **kwargs): - return user_request.has_perm('cotisations.change_facture_pdf'), "Vous ne pouvez pas éditer une facture sans droit trésorier" + return user_request.has_perm('cotisations.change_facture_pdf'), _("You don't have the right to edit an invoice.") def __init__(self, *args, **kwargs): super(Facture, self).__init__(*args, **kwargs) @@ -173,7 +206,9 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @receiver(post_save, sender=Facture) def facture_post_save(sender, **kwargs): - """Post save d'une facture, synchronise l'user ldap""" + """ + Synchronise the LDAP user after an invoice has been saved. + """ facture = kwargs['instance'] user = facture.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) @@ -181,52 +216,92 @@ 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""" + """ + Synchronise the LDAP user after an invoice has been deleted. + """ user = kwargs['instance'].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) +# TODO : change Vente to Purchase class Vente(RevMixin, AclMixin, models.Model): - """Objet vente, contient une quantité, une facture parente, un nom, - un prix. Peut-être relié à un objet cotisation, via le boolean - iscotisation""" - PRETTY_NAME = "Ventes effectuées" + """ + The model defining a purchase. It consist of one type of article being + sold. In particular there may be multiple purchases in a single invoice. + + It's reprensentated by: + * an amount (the number of items sold) + * an invoice (whose the purchase is part of) + * an article + * (if applicable) a cotisation (which holds some informations about + the effect of the purchase on the time agreed for this user) + """ + # TODO : change this to English COTISATION_TYPE = ( - ('Connexion', 'Connexion'), - ('Adhesion', 'Adhesion'), - ('All', 'All'), + ('Connexion', _l("Connexion")), + ('Adhesion', _l("Membership")), + ('All', _l("Both of them")), ) - 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) + # TODO : change facture to invoice + facture = models.ForeignKey( + 'Facture', + on_delete=models.CASCADE, + verbose_name=_l("Invoice") + ) + # TODO : change number to amount for clarity + number = models.IntegerField( + validators=[MinValueValidator(1)], + verbose_name=_l("Amount") + ) + # TODO : change this field for a ForeinKey to Article + name = models.CharField( + max_length=255, + verbose_name=_l("Article") + ) + # TODO : change prix to price + # TODO : this field is not needed if you use Article ForeignKey + prix = models.DecimalField( + max_digits=5, + decimal_places=2, + verbose_name=_l("Price")) + # TODO : this field is not needed if you use Article ForeignKey duration = models.PositiveIntegerField( - help_text="Durée exprimée en mois entiers", blank=True, - null=True) + null=True, + verbose_name=_l("Duration (in whole month)") + ) + # TODO : this field is not needed if you use Article ForeignKey type_cotisation = models.CharField( choices=COTISATION_TYPE, blank=True, null=True, - max_length=255 + max_length=255, + verbose_name=_l("Type of cotisation") ) class Meta: permissions = ( - ("view_vente", "Peut voir un objet vente"), - ("change_all_vente", "Superdroit, peut modifier toutes les ventes"), + ('view_vente', _l("Can see a purchase's details")), + ('change_all_vente', _l("Can edit all the previous purchases")), ) + verbose_name = _l("Purchase") + verbose_name_plural = _l("Purchases") + + # TODO : change prix_total to total_price def prix_total(self): - """Renvoie le prix_total de self (nombre*prix)""" + """ + Returns: the total of price for this amount of items. + """ 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""" + """ + Update the related object 'cotisation' if there is one. Based on the + duration of the purchase. + """ if hasattr(self, 'cotisation'): cotisation = self.cotisation cotisation.date_end = cotisation.date_start + relativedelta( @@ -234,9 +309,11 @@ class Vente(RevMixin, AclMixin, models.Model): return def create_cotis(self, date_start=False): - """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""" + """ + Update and create a 'cotisation' related object if there is a + cotisation_type defined (which means the article sold represents + a cotisation) + """ if not hasattr(self, 'cotisation') and self.type_cotisation: cotisation = Cotisation(vente=self) cotisation.type_cotisation = self.type_cotisation @@ -264,41 +341,44 @@ class Vente(RevMixin, AclMixin, models.Model): return def save(self, *args, **kwargs): - # On verifie que si iscotisation, duration est présent + """ + Save a purchase object and check if all the fields are coherents + It also update the associated cotisation in the changes have some + effect on the user's cotisation + """ + # Checking that if a cotisation is specified, there is also a duration if self.type_cotisation and not self.duration: - raise ValidationError("Cotisation et durée doivent être présents\ - ensembles") + raise ValidationError( + _("A cotisation should always have a duration.") + ) self.update_cotisation() super(Vente, self).save(*args, **kwargs) def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_vente'): - return False, u"Vous n'avez pas le droit d'éditer les ventes" + return False, _("You don't have the right to edit the purchases.") elif not user_request.has_perm('cotisations.change_all_facture') and not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" + return False, _("You don't have the right to edit this user's purchases.") elif not user_request.has_perm('cotisations.change_all_vente') and\ (self.facture.control or not self.facture.valid): - return False, u"Vous n'avez pas le droit d'éditer une vente\ - controlée ou invalidée par un trésorier" + return False, _("You don't have the right to edit a purchase already controlled or invalidated.") else: return True, None def can_delete(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.delete_vente'): - return False, u"Vous n'avez pas le droit de supprimer une vente" + return False, _("You don't have the right to delete a purchase.") if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer les factures de cet user protégé" + return False, _("You don't have the right to delete this user's purchases.") if self.facture.control or not self.facture.valid: - return False, u"Vous ne pouvez pas supprimer une vente\ - contrôlée ou invalidée par un trésorier" + return False, _("You don't have the right to delete a purchase already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.view_vente') and\ self.facture.user != user_request: - return False, u"Vous ne pouvez pas afficher l'historique d'une\ - facture d'un autre user que vous sans droit cableur" + return False, _("You don't have the right to see someone else's purchase history.") else: return True, None @@ -306,81 +386,109 @@ class Vente(RevMixin, AclMixin, models.Model): return str(self.name) + ' ' + str(self.facture) +# TODO : change vente to purchase @receiver(post_save, sender=Vente) def vente_post_save(sender, **kwargs): - """Post save d'une vente, déclencge la création de l'objet cotisation - si il y a lieu(si iscotisation) """ - vente = kwargs['instance'] + """ + Creates a 'cotisation' related object if needed and synchronise the + LDAP user when a purchase has been saved. + """ + purchase = kwargs['instance'] if hasattr(vente, 'cotisation'): - vente.cotisation.vente = vente - vente.cotisation.save() - if vente.type_cotisation: - vente.create_cotis() - vente.cotisation.save() - user = vente.facture.user + purchase.cotisation.vente = purchase + purchase.cotisation.save() + if purchase.type_cotisation: + purchase.create_cotis() + purchase.cotisation.save() + user = purchase.facture.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) +# TODO : change vente to purchase @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.type_cotisation: - user = vente.facture.user + """ + Synchronise the LDAP user after a purchase has been deleted. + """ + purchase = kwargs['instance'] + if purchase.type_cotisation: + user = purchase.facture.user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) class Article(RevMixin, AclMixin, models.Model): - """Liste des articles en vente : prix, nom, et attribut iscotisation - et duree si c'est une cotisation""" - PRETTY_NAME = "Articles en vente" + """ + The definition of an article model. It represents an type of object that can be sold to the user. + + It's represented by: + * a name + * a price + * a cotisation type (indicating if this article reprensents a cotisation or not) + * a duration (if it is a cotisation) + * a type of user (indicating what kind of user can buy this article) + """ + # TODO : Either use TYPE or TYPES in both choices but not both USER_TYPES = ( - ('Adherent', 'Adherent'), - ('Club', 'Club'), - ('All', 'All'), + ('Adherent', _l("Member")), + ('Club', _l("Club")), + ('All', _l("Both of them")), ) COTISATION_TYPE = ( - ('Connexion', 'Connexion'), - ('Adhesion', 'Adhesion'), - ('All', 'All'), + ('Connexion', _l("Connexion")), + ('Adhesion', _l("Membership")), + ('All', _l("Both of them")), ) - name = models.CharField(max_length=255) - prix = models.DecimalField(max_digits=5, decimal_places=2) + name = models.CharField( + max_length=255, + verbose_name=_l("Designation") + ) + # TODO : change prix to price + prix = models.DecimalField( + max_digits=5, + decimal_places=2, + verbose_name=_l("Unitary price") + ) duration = models.PositiveIntegerField( - help_text="Durée exprimée en mois entiers", blank=True, null=True, - validators=[MinValueValidator(0)]) + validators=[MinValueValidator(0)], + verbose_name=_l("Duration (in whole month)") + ) type_user = models.CharField( choices=USER_TYPES, default='All', - max_length=255 + max_length=255, + verbose_name=_l("Type of users concerned") ) type_cotisation = models.CharField( choices=COTISATION_TYPE, default=None, blank=True, null=True, - max_length=255 + max_length=255, + verbose_name=_l("Type of cotisation") ) unique_together = ('name', 'type_user') class Meta: permissions = ( - ("view_article", "Peut voir un objet article"), + ('view_article', _l("Can see an article's details")), ) + verbose_name = "Article" + verbose_name_plural = "Articles" def clean(self): - if self.name.lower() == "solde": - raise ValidationError("Solde est un nom d'article invalide") + if self.name.lower() == 'solde': + raise ValidationError( + _("Solde is a reserved article name") + ) if self.type_cotisation and not self.duration: raise ValidationError( - "La durée est obligatoire si il s'agit d'une cotisation" + _("Duration must be specified for a cotisation") ) def __str__(self): @@ -388,99 +496,150 @@ class Article(RevMixin, AclMixin, models.Model): class Banque(RevMixin, AclMixin, models.Model): - """Liste des banques""" - PRETTY_NAME = "Banques enregistrées" + """ + The model defining a bank. It represents a user's bank. It's mainly used + for statistics by regrouping the user under their bank's name and avoid + the use of a simple name which leads (by experience) to duplicates that + only differs by a capital letter, a space, a misspelling, ... That's why + it's easier to use simple object for the banks. + """ - name = models.CharField(max_length=255) + name = models.CharField( + max_length=255, + verbose_name=_l("Name") + ) class Meta: permissions = ( - ("view_banque", "Peut voir un objet banque"), + ('view_banque', _l("Can see a bank's details")), ) + verbose_name=_l("Bank") + verbose_name_plural=_l("Banks") def __str__(self): return self.name +# TODO : change Paiement to Payment class Paiement(RevMixin, AclMixin, models.Model): - """Moyens de paiement""" - PRETTY_NAME = "Moyens de paiement" + """ + The model defining a payment method. It is how the user is paying for the + invoice. It's easier to know this information when doing the accouts. + It is represented by: + * a name + * a type (used for the type 'cheque' which implies the use of a bank + and an account number in related models) + """ + PAYMENT_TYPES = ( - (0, 'Autre'), - (1, 'Chèque'), + (0, _l("Standard")), + (1, _l("Cheque")), ) - moyen = models.CharField(max_length=255) - type_paiement = models.IntegerField(choices=PAYMENT_TYPES, default=0) + # TODO : change moyen to method + moyen = models.CharField( + max_length=255, + verbose_name=_l("Method") + ) + type_paiement = models.IntegerField( + choices=PAYMENT_TYPES, + default=0, + verbose_name=_l("Payment type") + ) class Meta: permissions = ( - ("view_paiement", "Peut voir un objet paiement"), + ('view_paiement', _l("Can see a payement's details")), ) + verbose_name = _l("Payment method") + verbose_name_plural = _l("Payment methods") def __str__(self): return self.moyen def clean(self): + """ + Override of the herited clean function to get a correct name + """ self.moyen = self.moyen.title() def save(self, *args, **kwargs): - """Un seul type de paiement peut-etre cheque...""" + """ + Override of the herited save function to be sure only one payment + method of type 'cheque' exists. + """ if Paiement.objects.filter(type_paiement=1).count() > 1: - raise ValidationError("On ne peut avoir plusieurs mode de paiement\ - chèque") + raise ValidationError( + _("You cannot have multiple payment method of type cheque") + ) super(Paiement, self).save(*args, **kwargs) class Cotisation(RevMixin, AclMixin, models.Model): - """Objet cotisation, debut et fin, relié en onetoone à une vente""" - PRETTY_NAME = "Cotisations" + """ + The model defining a cotisation. It holds information about the time a user + is allowed when he has paid something. + It characterised by : + * a date_start (the date when the cotisaiton begins/began + * a date_end (the date when the cotisation ends/ended + * a type of cotisation (which indicates the implication of such + cotisation) + * a purchase (the related objects this cotisation is linked to) + """ COTISATION_TYPE = ( - ('Connexion', 'Connexion'), - ('Adhesion', 'Adhesion'), - ('All', 'All'), + ('Connexion', _l("Connexion")), + ('Adhesion', _l("Membership")), + ('All', _l("Both of them")), ) - vente = models.OneToOneField('Vente', on_delete=models.CASCADE, null=True) + # TODO : change vente to purchase + vente = models.OneToOneField( + 'Vente', + on_delete=models.CASCADE, + null=True, + verbose_name=_l("Purchase") + ) type_cotisation = models.CharField( choices=COTISATION_TYPE, max_length=255, default='All', + verbose_name=_l("Type of cotisation") + ) + date_start = models.DateTimeField( + verbose_name=_l("Starting date") + ) + date_end = models.DateTimeField( + verbose_name=_l("Ending date") ) - date_start = models.DateTimeField() - date_end = models.DateTimeField() class Meta: permissions = ( - ("view_cotisation", "Peut voir un objet cotisation"), - ("change_all_cotisation", "Superdroit, peut modifier toutes les cotisations"), + ('view_cotisation', _l("Can see a cotisation's details")), + ('change_all_cotisation', _l("Can edit the previous cotisations")), ) def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_cotisation'): - return False, u"Vous n'avez pas le droit d'éditer les cotisations" + return False, _("You don't have the right to edit a cotisation.") elif not user_request.has_perm('cotisations.change_all_cotisation') and\ (self.vente.facture.control or not self.vente.facture.valid): - return False, u"Vous n'avez pas le droit d'éditer une cotisation\ - controlée ou invalidée par un trésorier" + return False, _("You don't have the right to edit a cotisation already controlled or invalidated.") else: return True, None def can_delete(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.delete_cotisation'): - return False, u"Vous n'avez pas le droit de supprimer une cotisations" + return False, _("You don't have the right to delete a cotisation.") if self.vente.facture.control or not self.vente.facture.valid: - return False, u"Vous ne pouvez pas supprimer une cotisations\ - contrôlée ou invalidée par un trésorier" + return False, _("You don't have the right to delete a cotisation already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.view_cotisation') and\ self.vente.facture.user != user_request: - return False, u"Vous ne pouvez pas afficher l'historique d'une\ - cotisation d'un autre user que vous sans droit cableur" + return False, _("You don't have the right to see someone else's cotisation history.") else: return True, None @@ -490,16 +649,23 @@ class Cotisation(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Cotisation) def cotisation_post_save(sender, **kwargs): - """Après modification d'une cotisation, regeneration des services""" + """ + Mark some services as needing a regeneration after the edition of a + cotisation. Indeed the membership status may have changed. + """ regen('dns') regen('dhcp') regen('mac_ip_list') regen('mailing') +# TODO : should be name cotisation_post_delete @receiver(post_delete, sender=Cotisation) def vente_post_delete(sender, **kwargs): - """Après suppression d'une vente, régénération des services""" + """ + Mark some services as needing a regeneration after the deletion of a + cotisation. Indeed the membership status may have changed. + """ cotisation = kwargs['instance'] regen('mac_ip_list') regen('mailing') diff --git a/cotisations/payment.py b/cotisations/payment.py index f9a66bf6..0412ca94 100644 --- a/cotisations/payment.py +++ b/cotisations/payment.py @@ -8,6 +8,7 @@ from django.contrib.auth.decorators import login_required from django.contrib import messages from django.views.decorators.csrf import csrf_exempt from django.utils.datastructures import MultiValueDictKeyError +from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseBadRequest from collections import OrderedDict @@ -19,10 +20,15 @@ from .payment_utils.comnpay import Payment as ComnpayPayment @csrf_exempt @login_required def accept_payment(request, factureid): + """ + The view called when an online payment has been accepted. + """ facture = get_object_or_404(Facture, id=factureid) messages.success( request, - "Le paiement de {} € a été accepté.".format(facture.prix()) + _("The payment of %(amount)s € has been accepted.") % { + amount: facture.prix() + } ) return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) @@ -30,14 +36,22 @@ def accept_payment(request, factureid): @csrf_exempt @login_required def refuse_payment(request): + """ + The view called when an online payment has been refused. + """ messages.error( request, - "Le paiement a été refusé." + _("The payment has been refused.") ) return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) @csrf_exempt def ipn(request): + """ + The view called by Comnpay server to validate the transaction. + Verify that we can firmly save the user's action and notify + Comnpay with 400 response if not or with a 200 response if yes + """ p = ComnpayPayment() order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', ) try: @@ -52,7 +66,7 @@ def ipn(request): idTpe = request.POST['idTpe'] idTransaction = request.POST['idTransaction'] - # On vérifie que le paiement nous est destiné + # Checking that the payment is actually for us. if not idTpe == AssoOption.get_cached_value('payment_id'): return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") @@ -63,22 +77,28 @@ def ipn(request): facture = get_object_or_404(Facture, id=factureid) - # On vérifie que le paiement est valide + # Checking that the payment is valid if not result: - # Le paiement a échoué : on effectue les actions nécessaires (On indique qu'elle a échoué) + # Payment failed: Cancelling the invoice operation facture.delete() - - # On notifie au serveur ComNPay qu'on a reçu les données pour traitement + # And send the response to Comnpay indicating we have well + # received the failure information. return HttpResponse("HTTP/1.1 200 OK") facture.valid = True facture.save() - # A nouveau, on notifie au serveur qu'on a bien traité les données + # Everything worked we send a reponse to Comnpay indicating that + # it's ok for them to proceed return HttpResponse("HTTP/1.0 200 OK") def comnpay(facture, request): + """ + Build a request to start the negociation with Comnpay by using + a facture id, the price and the secret transaction data stored in + the preferences. + """ host = request.get_host() p = ComnpayPayment( str(AssoOption.get_cached_value('payment_id')), @@ -105,6 +125,7 @@ def comnpay(facture, request): return r +# The payment systems supported by re2o PAYMENT_SYSTEM = { 'COMNPAY' : comnpay, 'NONE' : None diff --git a/cotisations/templates/cotisations/aff_article.html b/cotisations/templates/cotisations/aff_article.html index 833b7de0..eb4ec0a7 100644 --- a/cotisations/templates/cotisations/aff_article.html +++ b/cotisations/templates/cotisations/aff_article.html @@ -23,15 +23,16 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load acl %} +{% load i18n %} - - - - - + + + + + @@ -44,11 +45,11 @@ with this program; if not, write to the Free Software Foundation, Inc., diff --git a/cotisations/templates/cotisations/aff_banque.html b/cotisations/templates/cotisations/aff_banque.html index 1ef4cb76..d9ec8d1c 100644 --- a/cotisations/templates/cotisations/aff_banque.html +++ b/cotisations/templates/cotisations/aff_banque.html @@ -23,11 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load acl %} +{% load i18n %}
ArticlePrixType CotisationDurée (mois)Article pour{% trans "Article" %}{% trans "Price" %}{% trans "Cotisation type" %}{% trans "Duration (month)" %}{% trans "Concerned users" %}
{{ article.type_user }} {% can_edit article %} - + {% acl_end %} - +
- + @@ -36,11 +37,11 @@ with this program; if not, write to the Free Software Foundation, Inc., diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index 48a856a2..fdb575ed 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -23,20 +23,34 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load acl %} +{% load i18n %} +
{% if facture_list.paginator %} -{% include "pagination.html" with list=facture_list %} +{% include 'pagination.html' with list=facture_list %} {% endif %}
Banque{% trans "Bank" %}
{{ banque.name }} {% can_edit banque %} - + {% acl_end %} - +
- - - - - - + + + + + + @@ -50,39 +64,49 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %}
{% include "buttons/sort.html" with prefix='cotis' col='user' text='Utilisateur' %}DesignationPrix 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' %} + {% trans "User" as tr_user %} + {% include 'buttons/sort.html' with prefix='cotis' col='user' text=tr_user %} + {% trans "Designation" %}{% trans "Total price" %} + {% trans "Payment method" as tr_payment_method %} + {% include 'buttons/sort.html' with prefix='cotis' col='paiement' text=tr_payment_method %} + + {% trans "Date" as tr_date %} + {% include 'buttons/sort.html' with prefix='cotis' col='date' text=tr_date %} + + {% trans "Invoice id" as tr_invoice_id %} + {% include 'buttons/sort.html' with prefix='cotis' col='id' text=tr_invoice_id %} +
{{ facture.date }} {{ facture.id }} - + - {% if facture.valid %} + {% if facture.valid %} - - PDF + {% trans "PDF" %} - {% else %} - Facture invalide - {% endif %} + {% else %} + {% trans "Invalidated invoice" %} + {% endif %}
{% if facture_list.paginator %} -{% include "pagination.html" with list=facture_list %} +{% include 'pagination.html' with list=facture_list %} {% endif %} diff --git a/cotisations/templates/cotisations/aff_paiement.html b/cotisations/templates/cotisations/aff_paiement.html index 09e5acc3..63a5cf35 100644 --- a/cotisations/templates/cotisations/aff_paiement.html +++ b/cotisations/templates/cotisations/aff_paiement.html @@ -23,11 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load acl %} +{% load i18n %} - + @@ -35,12 +36,12 @@ with this program; if not, write to the Free Software Foundation, Inc., diff --git a/cotisations/templates/cotisations/control.html b/cotisations/templates/cotisations/control.html index 48071429..cdd212e3 100644 --- a/cotisations/templates/cotisations/control.html +++ b/cotisations/templates/cotisations/control.html @@ -25,13 +25,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load staticfiles%} +{% load i18n %} -{% block title %}Controle des factures{% endblock %} +{% block title %}{% trans "Invoice control" %}{% endblock %} {% block content %} -

Controle et validité des factures

+

{% trans "Invoice control and validation" %}

{% if facture_list.paginator %} -{% include "pagination.html" with list=facture_list %} +{% include 'pagination.html' with list=facture_list %} {% endif %} {% csrf_token %} @@ -39,24 +40,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Moyen de paiement{% trans "Payment method" %}
{{ paiement.moyen }} - {% can_edit paiement %} - + {% can_edit paiement %} + - {% acl_end %} - + {% acl_end %} +
- - - - - - - - - - - + + + + + + + + + {% for form in controlform.forms %} {% bootstrap_form_errors form %} - - @@ -74,10 +101,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %}
Profil{% include "buttons/sort.html" with prefix='control' col='name' text='Nom' %}{% include "buttons/sort.html" with prefix='control' col='surname' text='Prénom' %}{% include "buttons/sort.html" with prefix='control' col='id' text='Id facture' %}{% include "buttons/sort.html" with prefix='control' col='user-id' text='Id user' %}DesignationPrix total{% include "buttons/sort.html" with prefix='control' col='paiement' text='Moyen de paiement' %}{% include "buttons/sort.html" with prefix='control' col='date' text='Date' %}{% include "buttons/sort.html" with prefix='control' col='valid' text='Valide' %}{% include "buttons/sort.html" with prefix='control' col='control' text='Contrôlée' %}{% trans "Profil" %} + {% trans "Last name" as tr_last_name %} + {% include 'buttons/sort.html' with prefix='control' col='name' text=tr_last_name %} + + {% trans "First name" as tr_first_name %} + {% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_first_name %} + + {% trans "Invoice id" as tr_invoice_id %} + {% include 'buttons/sort.html' with prefix='control' col='id' text=tr_invoice_id %} + + {% trans "User id" as tr_user_id %} + {% include 'buttons/sort.html' with prefix='control' col='user-id' text=tr_user_id %} + {% trans "Designation" %}{% trans "Total price" %} + {% trans "Payment method" as tr_payment_method %} + {% include 'buttons/sort.html' with prefix='control' col='paiement' text=tr_payment_method %} + + {% trans "Date" as tr_date %} + {% include 'buttons/sort.html' with prefix='control' col='date' text=tr_date %}< + /th> + + {% trans "Validated" as tr_validated %} + {% include 'buttons/sort.html' with prefix='control' col='valid' text=tr_validated %}< + /th> + + {% trans "Controlled" as tr_controlled %} + {% include 'buttons/sort.html' with prefix='control' col='control' text=tr_controlled %} +
+ + + + {{ form.instance.user.name }} {{ form.instance.user.surname }}
- {% bootstrap_button "Modifier" button_type="submit" icon="star" %} + {% trans "Edit" as tr_edit %} + {% bootstrap_button tr_edit button_type='submit' icon='star' %} {% endblock %} {% if facture_list.paginator %} -{% include "pagination.html" with list=facture_list %} +{% include 'pagination.html' with list=facture_list %} {% endif %} diff --git a/cotisations/templates/cotisations/delete.html b/cotisations/templates/cotisations/delete.html index 318be65d..a1c95d7a 100644 --- a/cotisations/templates/cotisations/delete.html +++ b/cotisations/templates/cotisations/delete.html @@ -24,17 +24,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} +{% load i18n %} -{% block title %}Création et modification de machines{% endblock %} +{% block title %}{% trans "Deletion of cotisations" %}{% endblock %} {% block content %}
{% csrf_token %} -

Attention, voulez-vous vraiment supprimer cet objet {{ objet_name }} ( {{ objet }} ) ?

- {% bootstrap_button "Confirmer" button_type="submit" icon="trash" %} +

+ {% blocktrans %} + Warning. Are you sure you really want te delete this {{ object_name }} object ( {{ objet }} ) ? + {% endblocktrans %} +

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type='submit' icon='trash' %}
-
-
-
{% endblock %} diff --git a/cotisations/templates/cotisations/edit_facture.html b/cotisations/templates/cotisations/edit_facture.html index f1af2b8b..d28f8511 100644 --- a/cotisations/templates/cotisations/edit_facture.html +++ b/cotisations/templates/cotisations/edit_facture.html @@ -26,27 +26,28 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load staticfiles%} {% load massive_bootstrap_form %} +{% load i18n %} -{% block title %}Création et modification de factures{% endblock %} +{% block title %}{% trans "Invoices creation and edition" %}{% endblock %} {% block content %} {% bootstrap_form_errors factureform %}
{% csrf_token %} -

Editer la facture

+

{% trans "Edit the invoice" %}

{% massive_bootstrap_form factureform 'user' %} {{ venteform.management_form }} -

Articles de la facture

+

{% trans "Invoice's articles" %}

- - + + - {% for form in venteform.forms %} - {% bootstrap_form_errors form %} + {% for form in venteform.forms %} + {% bootstrap_form_errors form %} @@ -56,7 +57,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endfor %}
DésignationQuantité{% trans "Designation" %}{% trans "Quantity" %}
{{ form.name }} {{ form.number }}
- {% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %} + {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type='submit' icon='star' %}
{% endblock %} diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index 5ed3e94e..2b0cd456 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -25,8 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load staticfiles%} +{% load i18n %} -{% block title %}Création et modification de factures{% endblock %} +{% block title %}{% trans "Invoices creation and edition" %}{% endblock %} {% block content %} {% bootstrap_form_errors factureform %} @@ -34,7 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} {% bootstrap_form factureform %} - {% bootstrap_button action_name button_type="submit" icon="star" %} + {% bootstrap_button action_name button_type='submit' icon='star' %}
{% endblock %} diff --git a/cotisations/templates/cotisations/index.html b/cotisations/templates/cotisations/index.html index 52ff4689..9482cb5a 100644 --- a/cotisations/templates/cotisations/index.html +++ b/cotisations/templates/cotisations/index.html @@ -24,14 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} +{% load i18n %} -{% block title %}Facture{% endblock %} +{% block title %}{% trans "Invoices" %}{% endblock %} {% block content %} -

Cotisations

- {% include "cotisations/aff_cotisations.html" with facture_list=facture_list %} -
-
-
+

{% trans "Cotisations" %}

+ {% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %} {% endblock %} diff --git a/cotisations/templates/cotisations/index_article.html b/cotisations/templates/cotisations/index_article.html index 7803c2ca..5e6c3967 100644 --- a/cotisations/templates/cotisations/index_article.html +++ b/cotisations/templates/cotisations/index_article.html @@ -25,18 +25,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} +{% load i18n %} -{% block title %}Articles{% endblock %} +{% block title %}{% trans "Articles" %}{% endblock %} {% block content %} -

Liste des types d'articles

- {% can_create Article %} - Ajouter un type d'articles - {% acl_end %} - Supprimer un ou plusieurs types d'articles - {% include "cotisations/aff_article.html" with article_list=article_list %} -
-
-
+

{% trans "Article types list" %}

+ {% can_create Article %} + + {% trans "Add an article type" %} + + {% acl_end %} + + {% trans "Delete article types" %} + + {% include 'cotisations/aff_article.html' with article_list=article_list %} {% endblock %} diff --git a/cotisations/templates/cotisations/index_banque.html b/cotisations/templates/cotisations/index_banque.html index 77c23977..e9118d75 100644 --- a/cotisations/templates/cotisations/index_banque.html +++ b/cotisations/templates/cotisations/index_banque.html @@ -25,18 +25,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} +{% load i18n %} -{% block title %}Banques{% endblock %} +{% block title %}{% trans "Banks" %}{% endblock %} {% block content %} -

Liste des banques

- {% can_create Banque %} - Ajouter une banque - {% acl_end %} - Supprimer une ou plusieurs banques - {% include "cotisations/aff_banque.html" with banque_list=banque_list %} -
-
-
+

{% trans "Banks list" %}

+ {% can_create Banque %} + + {% trans "Add a bank" %} + + {% acl_end %} + + {% trans "Delete banks" %} + + {% include 'cotisations/aff_banque.html' with banque_list=banque_list %} {% endblock %} diff --git a/cotisations/templates/cotisations/index_paiement.html b/cotisations/templates/cotisations/index_paiement.html index 414c6b38..d84c72eb 100644 --- a/cotisations/templates/cotisations/index_paiement.html +++ b/cotisations/templates/cotisations/index_paiement.html @@ -25,18 +25,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} +{% load i18n %} -{% block title %}Paiements{% endblock %} +{% block title %}{% trans "Payments" %}{% endblock %} {% block content %} -

Liste des types de paiements

- {% can_create Paiement %} - Ajouter un type de paiement - {% acl_end %} - Supprimer un ou plusieurs types de paiements - {% include "cotisations/aff_paiement.html" with paiement_list=paiement_list %} -
-
-
+

{% trans "Payment types list" %}

+ {% can_create Paiement %} + + {% trans "Add a payment type" %} + + {% acl_end %} + + {% trans "Delete payment types" %} + + {% include 'cotisations/aff_paiement.html' with paiement_list=paiement_list %} {% endblock %} diff --git a/cotisations/templates/cotisations/new_facture.html b/cotisations/templates/cotisations/new_facture.html index 10aa69fd..4dab92d3 100644 --- a/cotisations/templates/cotisations/new_facture.html +++ b/cotisations/templates/cotisations/new_facture.html @@ -25,40 +25,45 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load staticfiles%} +{% load i18n %} -{% block title %}Création et modification de factures{% endblock %} +{% block title %}{% trans "Invoices creation and edition" %}{% endblock %} {% block content %} {% bootstrap_form_errors factureform %}
{% csrf_token %} -

Nouvelle facture

+

{% trans "New invoice" %}

- Solde de l'utilisateur : {{ user.solde }} € + {% blocktrans %} + User's balance : {{ user.solde }} € + {% endblocktrans %}

{% bootstrap_form factureform %} {{ venteform.management_form }} -

Articles de la facture

+

{% trans "Invoice's articles" %}

- {% for form in venteform.forms %} + {% for form in venteform.forms %}
- Article :   - {% bootstrap_form form label_class='sr-only' %} -   - + {% trans "Article" %} :   + {% bootstrap_form form label_class='sr-only' %} +   +
- {% endfor %} + {% endfor %}
- +

- Prix total : 0,00 € + {% blocktrans %} + Total price : 0,00 € + {% endblocktrans %}

- {% bootstrap_button "Créer" button_type="submit" icon="star" %} + {% trans "Create" as tr_create %} + {% bootstrap_button tr_create button_type='submit' icon='star' %}