forms.py 8.93 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 44

from django.utils.translation import ugettext_lazy as _
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

87
class SelectArticleForm(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
        queryset=Article.objects.none(),
94
        label=_("Article"),
95 96 97
        required=True
    )
    quantity = forms.IntegerField(
98
        label=_("Quantity"),
99 100 101 102
        validators=[MinValueValidator(1)],
        required=True
    )

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

109

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
110
class CustomInvoiceForm(FormRevMixin, ModelForm):
111
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
112
    Form used to create a custom invoice.
113
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
114 115 116
    class Meta:
        model = CustomInvoice
        fields = '__all__'
117

chirac's avatar
chirac committed
118

119
class ArticleForm(FormRevMixin, ModelForm):
120 121 122
    """
    Form used to create an article.
    """
123 124 125 126 127
    class Meta:
        model = Article
        fields = '__all__'

    def __init__(self, *args, **kwargs):
128
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
129
        super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs)
130
        self.fields['name'].label = _("Article name")
131

chirac's avatar
chirac committed
132

133
class DelArticleForm(FormRevMixin, Form):
134 135 136 137
    """
    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
138
    articles = forms.ModelMultipleChoiceField(
139
        queryset=Article.objects.none(),
140
        label=_("Available articles"),
chirac's avatar
chirac committed
141 142 143
        widget=forms.CheckboxSelectMultiple
    )

144 145 146 147 148 149 150 151
    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()

152

153
# TODO : change Paiement to Payment
154
class PaiementForm(FormRevMixin, ModelForm):
155 156 157 158 159
    """
    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.
    """
160 161
    class Meta:
        model = Paiement
162
        # TODO : change moyen to method and type_paiement to payment_type
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
163
        fields = ['moyen', 'available_for_everyone']
164 165

    def __init__(self, *args, **kwargs):
166
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
167
        super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs)
168
        self.fields['moyen'].label = _("Payment method name")
169

chirac's avatar
chirac committed
170

171
# TODO : change paiement to payment
172
class DelPaiementForm(FormRevMixin, Form):
173 174 175 176
    """
    Form used to delete one or more payment methods.
    The user must choose the one to delete by checking the boxes.
    """
177
    # TODO : change paiement to payment
chirac's avatar
chirac committed
178
    paiements = forms.ModelMultipleChoiceField(
179
        queryset=Paiement.objects.none(),
180
        label=_("Available payment methods"),
chirac's avatar
chirac committed
181 182 183
        widget=forms.CheckboxSelectMultiple
    )

184 185 186 187 188 189 190 191
    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()

192

193
# TODO : change banque to bank
194
class BanqueForm(FormRevMixin, ModelForm):
195 196 197
    """
    Form used to create a bank.
    """
chirac's avatar
chirac committed
198
    class Meta:
199
        # TODO : change banque to bank
chirac's avatar
chirac committed
200 201 202 203
        model = Banque
        fields = ['name']

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

chirac's avatar
chirac committed
208

209
# TODO : change banque to bank
210
class DelBanqueForm(FormRevMixin, Form):
211 212 213 214
    """
    Form used to delete one or more banks.
    The use must choose the one to delete by checking the boxes.
    """
215
    # TODO : change banque to bank
chirac's avatar
chirac committed
216
    banques = forms.ModelMultipleChoiceField(
217
        queryset=Banque.objects.none(),
218
        label=_("Available banks"),
chirac's avatar
chirac committed
219 220
        widget=forms.CheckboxSelectMultiple
    )
221 222 223 224 225 226 227 228

    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()
229 230


231
# TODO : Better name and docstring
232
class RechargeForm(FormRevMixin, Form):
233 234 235
    """
    Form used to refill a user's balance
    """
236
    value = forms.FloatField(
237
        label=_("Amount"),
238
        min_value=0.01,
239
        validators=[]
240
    )
241 242
    payment = forms.ModelChoiceField(
        queryset=Paiement.objects.none(),
243
        label=_("Payment method")
244
    )
245

246
    def __init__(self, *args, user=None, user_source=None, **kwargs):
247
        self.user = user
248
        super(RechargeForm, self).__init__(*args, **kwargs)
249 250
        self.fields['payment'].empty_label = \
            _("Select a payment method")
251
        self.fields['payment'].queryset = Paiement.find_allowed_payments(user_source).exclude(is_balance=True)
252

253
    def clean(self):
254
        """
255
        Returns a cleaned value from the received form by validating
256 257
        the value is well inside the possible limits
        """
258
        value = self.cleaned_data['value']
Maël Kervella's avatar
Maël Kervella committed
259
        balance_method = get_object_or_404(balance.PaymentMethod)
260 261
        if balance_method.maximum_balance is not None and \
           value + self.user.solde > balance_method.maximum_balance:
262 263
            raise forms.ValidationError(
                _("Requested amount is too high. Your balance can't exceed \
264
                %(max_online_balance)s €.") % {
265
                    'max_online_balance': balance_method.maximum_balance
266 267
                }
            )
268
        return self.cleaned_data
269