forms.py 9.61 KB
Newer Older
1 2 3 4 5 6 7
# 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 © 2017  Gabriel Détraz
# Copyright © 2017  Goulven Kermarec
# Copyright © 2017  Augustin Lemesle
8
# Copyright © 2018  Hugo Levy-Falk
9 10 11 12 13 14 15 16 17 18 19 20 21 22
#
# 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.
23
"""
24
Forms for the 'cotisation' app of re2o. It highly depends on
25 26 27 28 29 30 31 32 33 34 35
:cotisations:models and is mainly used by :cotisations:views.

The following forms are mainly used to create, edit or delete
anything related to 'cotisations' :
    * Payments Methods
    * Banks
    * Invoices
    * Articles

See the details for each of these operations in the documentation
of each of the method.
36
"""
37 38
from __future__ import unicode_literals

39
from django import forms
40
from django.db.models import Q
Dalahro's avatar
Dalahro committed
41
from django.forms import ModelForm, Form
42
from django.core.validators import MinValueValidator
43
from django.utils.translation import ugettext as _
44
from django.utils.translation import ugettext_lazy as _l
45
from django.shortcuts import get_object_or_404
46

47
from re2o.field_permissions import FieldPermissionFormMixin
48
from re2o.mixins import FormRevMixin
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
49
from .models import Article, Paiement, Facture, Banque, CustomInvoice
50
from .payment_methods import balance
51

52

53
class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
54
    """
55
    Form used to manage and create an invoice and its fields.
56
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
57

58 59
    def __init__(self, *args, creation=False, **kwargs):
        user = kwargs['user']
60
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
61
        super(FactureForm, self).__init__(*args, prefix=prefix, **kwargs)
62 63
        self.fields['paiement'].empty_label = \
            _("Select a payment method")
64
        self.fields['paiement'].queryset = Paiement.find_allowed_payments(user)
65 66 67 68 69 70 71
        if not creation:
            self.fields['user'].label = _("Member")
            self.fields['user'].empty_label = \
                _("Select the proprietary member")
            self.fields['valid'].label = _("Validated invoice")
        else:
            self.fields = {'paiement': self.fields['paiement']}
72

73 74
    class Meta:
        model = Facture
75
        fields = '__all__'
76 77

    def clean(self):
78
        cleaned_data = super(FactureForm, self).clean()
79
        paiement = cleaned_data.get('paiement')
80
        if not paiement:
81
            raise forms.ValidationError(
82
                _("A payment method must be specified.")
83
            )
84 85
        return cleaned_data

chirac's avatar
chirac committed
86

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
87
class SelectUserArticleForm(FormRevMixin, Form):
88
    """
89 90
    Form used to select an article during the creation of an invoice for a
    member.
91
    """
chirac's avatar
chirac committed
92
    article = forms.ModelChoiceField(
93 94 95
        queryset=Article.objects.filter(
            Q(type_user='All') | Q(type_user='Adherent')
        ),
96
        label=_l("Article"),
97 98 99
        required=True
    )
    quantity = forms.IntegerField(
100
        label=_l("Quantity"),
101 102 103 104
        validators=[MinValueValidator(1)],
        required=True
    )

105
    def __init__(self, *args, **kwargs):
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
106
        user = kwargs.pop('user')
107
        super(SelectUserArticleForm, self).__init__(*args, **kwargs)
108
        self.fields['article'].queryset = Article.find_allowed_articles(user)
109

110 111

class SelectClubArticleForm(Form):
112
    """
113 114
    Form used to select an article during the creation of an invoice for a
    club.
115
    """
116
    article = forms.ModelChoiceField(
117 118 119
        queryset=Article.objects.filter(
            Q(type_user='All') | Q(type_user='Club')
        ),
120
        label=_l("Article"),
chirac's avatar
chirac committed
121 122 123
        required=True
    )
    quantity = forms.IntegerField(
124
        label=_l("Quantity"),
chirac's avatar
chirac committed
125 126 127 128
        validators=[MinValueValidator(1)],
        required=True
    )

129
    def __init__(self, user, *args, **kwargs):
130
        super(SelectClubArticleForm, self).__init__(*args, **kwargs)
131
        self.fields['article'].queryset = Article.find_allowed_articles(user)
132

133

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
134
class CustomInvoiceForm(FormRevMixin, ModelForm):
135
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
136
    Form used to create a custom invoice.
137
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
138 139 140
    class Meta:
        model = CustomInvoice
        fields = '__all__'
141

chirac's avatar
chirac committed
142

143
class ArticleForm(FormRevMixin, ModelForm):
144 145 146
    """
    Form used to create an article.
    """
147 148 149 150 151
    class Meta:
        model = Article
        fields = '__all__'

    def __init__(self, *args, **kwargs):
152
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
153
        super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs)
154
        self.fields['name'].label = _("Article name")
155

chirac's avatar
chirac committed
156

157
class DelArticleForm(FormRevMixin, Form):
158 159 160 161
    """
    Form used to delete one or more of the currently available articles.
    The user must choose the one to delete by checking the boxes.
    """
chirac's avatar
chirac committed
162
    articles = forms.ModelMultipleChoiceField(
163
        queryset=Article.objects.none(),
164
        label=_l("Existing articles"),
chirac's avatar
chirac committed
165 166 167
        widget=forms.CheckboxSelectMultiple
    )

168 169 170 171 172 173 174 175
    def __init__(self, *args, **kwargs):
        instances = kwargs.pop('instances', None)
        super(DelArticleForm, self).__init__(*args, **kwargs)
        if instances:
            self.fields['articles'].queryset = instances
        else:
            self.fields['articles'].queryset = Article.objects.all()

176

177
# TODO : change Paiement to Payment
178
class PaiementForm(FormRevMixin, ModelForm):
179 180 181 182 183
    """
    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.
    """
184 185
    class Meta:
        model = Paiement
186
        # TODO : change moyen to method and type_paiement to payment_type
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
187
        fields = ['moyen', 'available_for_everyone']
188 189

    def __init__(self, *args, **kwargs):
190
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
191
        super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs)
192
        self.fields['moyen'].label = _("Payment method name")
193

chirac's avatar
chirac committed
194

195
# TODO : change paiement to payment
196
class DelPaiementForm(FormRevMixin, Form):
197 198 199 200
    """
    Form used to delete one or more payment methods.
    The user must choose the one to delete by checking the boxes.
    """
201
    # TODO : change paiement to payment
chirac's avatar
chirac committed
202
    paiements = forms.ModelMultipleChoiceField(
203
        queryset=Paiement.objects.none(),
204
        label=_l("Existing payment method"),
chirac's avatar
chirac committed
205 206 207
        widget=forms.CheckboxSelectMultiple
    )

208 209 210 211 212 213 214 215
    def __init__(self, *args, **kwargs):
        instances = kwargs.pop('instances', None)
        super(DelPaiementForm, self).__init__(*args, **kwargs)
        if instances:
            self.fields['paiements'].queryset = instances
        else:
            self.fields['paiements'].queryset = Paiement.objects.all()

216

217
# TODO : change banque to bank
218
class BanqueForm(FormRevMixin, ModelForm):
219 220 221
    """
    Form used to create a bank.
    """
chirac's avatar
chirac committed
222
    class Meta:
223
        # TODO : change banque to bank
chirac's avatar
chirac committed
224 225 226 227
        model = Banque
        fields = ['name']

    def __init__(self, *args, **kwargs):
228
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
229
        super(BanqueForm, self).__init__(*args, prefix=prefix, **kwargs)
230
        self.fields['name'].label = _("Bank name")
chirac's avatar
chirac committed
231

chirac's avatar
chirac committed
232

233
# TODO : change banque to bank
234
class DelBanqueForm(FormRevMixin, Form):
235 236 237 238
    """
    Form used to delete one or more banks.
    The use must choose the one to delete by checking the boxes.
    """
239
    # TODO : change banque to bank
chirac's avatar
chirac committed
240
    banques = forms.ModelMultipleChoiceField(
241
        queryset=Banque.objects.none(),
242
        label=_l("Existing banks"),
chirac's avatar
chirac committed
243 244
        widget=forms.CheckboxSelectMultiple
    )
245 246 247 248 249 250 251 252

    def __init__(self, *args, **kwargs):
        instances = kwargs.pop('instances', None)
        super(DelBanqueForm, self).__init__(*args, **kwargs)
        if instances:
            self.fields['banques'].queryset = instances
        else:
            self.fields['banques'].queryset = Banque.objects.all()
253 254


255
# TODO : Better name and docstring
256
class RechargeForm(FormRevMixin, Form):
257 258 259
    """
    Form used to refill a user's balance
    """
260
    value = forms.FloatField(
261
        label=_l("Amount"),
262
        min_value=0.01,
263
        validators=[]
264
    )
265 266 267 268
    payment = forms.ModelChoiceField(
        queryset=Paiement.objects.none(),
        label=_l("Payment method")
    )
269

270 271
    def __init__(self, *args, user=None, **kwargs):
        self.user = user
272
        super(RechargeForm, self).__init__(*args, **kwargs)
273 274
        self.fields['payment'].empty_label = \
            _("Select a payment method")
275
        self.fields['payment'].queryset = Paiement.find_allowed_payments(user)
276

277
    def clean(self):
278
        """
279
        Returns a cleaned value from the received form by validating
280 281
        the value is well inside the possible limits
        """
282
        value = self.cleaned_data['value']
Maël Kervella's avatar
Maël Kervella committed
283
        balance_method = get_object_or_404(balance.PaymentMethod)
284 285
        if balance_method.maximum_balance is not None and \
           value + self.user.solde > balance_method.maximum_balance:
286 287
            raise forms.ValidationError(
                _("Requested amount is too high. Your balance can't exceed \
288
                %(max_online_balance)s €.") % {
289
                    'max_online_balance': balance_method.maximum_balance
290 291
                }
            )
292
        return self.cleaned_data