views.py 24.4 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 23
#
# 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.

24 25 26
# App de gestion des users pour re2o
# Goulven Kermarec, Gabriel Détraz
# Gplv2
27 28 29 30
"""cotisations.views
The different views used in the Cotisations module
"""

31
from __future__ import unicode_literals
chirac's avatar
chirac committed
32
import os
33 34

from django.urls import reverse
35
from django.shortcuts import render, redirect, get_object_or_404
36
from django.contrib.auth.decorators import login_required
37
from django.contrib import messages
chirac's avatar
chirac committed
38
from django.db.models import ProtectedError
39
from django.db.models import Q
Dalahro's avatar
Dalahro committed
40
from django.forms import modelformset_factory, formset_factory
chirac's avatar
chirac committed
41
from django.utils import timezone
42
from django.utils.translation import ugettext as _
43

chirac's avatar
chirac committed
44
# Import des models, forms et fonctions re2o
45
from reversion import revisions as reversion
chirac's avatar
chirac committed
46 47 48 49
from users.models import User
from re2o.settings import LOGO_PATH
from re2o import settings
from re2o.views import form
50
from re2o.utils import SortTable, re2o_paginator
51
from re2o.acl import (
52 53 54 55
    can_create,
    can_edit,
    can_delete,
    can_view,
56 57 58
    can_view_all,
    can_delete_set,
    can_change,
59
)
60
from preferences.models import AssoOption, GeneralOption
chirac's avatar
chirac committed
61
from .models import Facture, Article, Vente, Paiement, Banque
62
from .forms import (
63
    FactureForm,
64 65 66 67 68 69 70 71 72
    ArticleForm,
    DelArticleForm,
    PaiementForm,
    DelPaiementForm,
    BanqueForm,
    DelBanqueForm,
    NewFactureFormPdf,
    SelectUserArticleForm,
    SelectClubArticleForm,
73
    RechargeForm
74
)
75
from .tex import render_invoice
76
from .payment_methods.forms import payment_method_factory
77
from .utils import find_payment_method
78

79

chirac's avatar
chirac committed
80
@login_required
81 82 83
@can_create(Facture)
@can_edit(User)
def new_facture(request, user, userid):
84 85 86
    """
    View called to create a new invoice.
    Currently, Send the list of available articles for the user along with
87
    a formset of a new invoice (based on the `:forms:FactureForm()` form.
88 89 90 91 92 93
    A bit of JS is used in the template to add articles in a fancier way.
    If everything is correct, save each one of the articles, save the
    purchase object associated and finally the newly created invoice.
    """
    invoice = Facture(user=user)
    # The template needs the list of articles (for the JS part)
94 95 96
    article_list = Article.objects.filter(
        Q(type_user='All') | Q(type_user=request.user.class_name)
    )
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
97
    # Building the invoice form and the article formset
98
    invoice_form = FactureForm(
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
99 100
        request.POST or None,
        instance=invoice,
101 102
        user=request.user,
        creation=True
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
103
    )
104

105
    if request.user.is_class_club:
106
        article_formset = formset_factory(SelectClubArticleForm)(
107
            request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
108
            form_kwargs={'user': request.user}
109
        )
110
    else:
111
        article_formset = formset_factory(SelectUserArticleForm)(
112
            request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
113
            form_kwargs={'user': request.user}
114
        )
115 116 117

    if invoice_form.is_valid() and article_formset.is_valid():
        new_invoice_instance = invoice_form.save(commit=False)
118
        articles = article_formset
119
        # Check if at leat one article has been selected
120
        if any(art.cleaned_data for art in articles):
121
            # Building a purchase for each article sold
122
            purchases = []
123
            total_price = 0
124 125 126 127
            for art_item in articles:
                if art_item.cleaned_data:
                    article = art_item.cleaned_data['article']
                    quantity = art_item.cleaned_data['quantity']
128 129
                    total_price += article.prix*quantity
                    new_purchase = Vente(
130
                        facture=new_invoice_instance,
chirac's avatar
chirac committed
131 132
                        name=article.name,
                        prix=article.prix,
133
                        type_cotisation=article.type_cotisation,
chirac's avatar
chirac committed
134 135 136
                        duration=article.duration,
                        number=quantity
                    )
137 138
                    purchases.append(new_purchase)
            p = find_payment_method(new_invoice_instance.paiement)
139 140 141
            if hasattr(p, 'check_price'):
                price_ok, msg = p.check_price(total_price, user)
                invoice_form.add_error(None, msg)
142
            else:
143 144 145 146 147 148 149
                price_ok = True
            if price_ok:
                new_invoice_instance.save()
                for p in purchases:
                    p.facture = new_invoice_instance
                    p.save()

150 151 152 153 154 155 156 157
                return new_invoice_instance.paiement.end_payment(
                    new_invoice_instance,
                    request
                )
        else:
            messages.error(
                request,
                _("You need to choose at least one article.")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
158
            )
159 160 161 162 163
    p = Paiement.objects.filter(is_balance=True)
    if len(p) and p[0].can_use_payment(request.user):
        balance = user.solde
    else:
        balance = None
164 165
    return form(
        {
166
            'factureform': invoice_form,
167 168 169 170
            'articlesformset': article_formset,
            'articlelist': article_list,
            'balance': balance,
            'action_name': _('Create'),
171
        },
172
        'cotisations/facture.html', request
173
    )
174

175

176
# TODO : change facture to invoice
chirac's avatar
chirac committed
177
@login_required
178
@can_change(Facture, 'pdf')
179
def new_facture_pdf(request):
180
    """
181
    View used to generate a custom PDF invoice. It's mainly used to
182 183 184
    get invoices that are not taken into account, for the administrative
    point of view.
    """
185 186 187 188 189
    # The template needs the list of articles (for the JS part)
    articles = Article.objects.filter(
        Q(type_user='All') | Q(type_user=request.user.class_name)
    )
    # Building the invocie form and the article formset
190
    invoice_form = NewFactureFormPdf(request.POST or None)
191
    if request.user.is_class_club:
192
        articles_formset = formset_factory(SelectClubArticleForm)(
193 194
            request.POST or None,
            form_kwargs={'user': request.user}
195
        )
196
    else:
197
        articles_formset = formset_factory(SelectUserArticleForm)(
198 199
            request.POST or None,
            form_kwargs={'user': request.user}
200
        )
201 202 203 204 205 206 207 208 209 210 211 212 213 214
    if invoice_form.is_valid() and articles_formset.is_valid():
        # Get the article list and build an list out of it
        # contiaining (article_name, article_price, quantity, total_price)
        articles_info = []
        for articles_form in articles_formset:
            if articles_form.cleaned_data:
                article = articles_form.cleaned_data['article']
                quantity = articles_form.cleaned_data['quantity']
                articles_info.append({
                    'name': article.name,
                    'price': article.prix,
                    'quantity': quantity,
                    'total_price': article.prix * quantity
                })
215 216 217
        paid = invoice_form.cleaned_data['paid']
        recipient = invoice_form.cleaned_data['dest']
        address = invoice_form.cleaned_data['chambre']
218 219
        total_price = sum(a['total_price'] for a in articles_info)

220
        return render_invoice(request, {
221
            'DATE': timezone.now(),
222 223
            'recipient_name': recipient,
            'address': address,
224
            'article': articles_info,
225
            'total': total_price,
226
            'paid': paid,
227 228 229 230 231 232
            'asso_name': AssoOption.get_cached_value('name'),
            'line1': AssoOption.get_cached_value('adresse1'),
            'line2': AssoOption.get_cached_value('adresse2'),
            'siret': AssoOption.get_cached_value('siret'),
            'email': AssoOption.get_cached_value('contact'),
            'phone': AssoOption.get_cached_value('telephone'),
233
            'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
234
        })
235
    return form({
236
        'factureform': invoice_form,
237 238 239
        'action_name': _("Create"),
        'articlesformset': articles_formset,
        'articles': articles
240
    }, 'cotisations/facture.html', request)
241

242

243
# TODO : change facture to invoice
Dalahro's avatar
Dalahro committed
244
@login_required
245
@can_view(Facture)
246
def facture_pdf(request, facture, **_kwargs):
247 248 249 250 251 252
    """
    View used to generate a PDF file from  an existing invoice in database
    Creates a line for each Purchase (thus article sold) and generate the
    invoice with the total price, the payment method, the address and the
    legal information for the user.
    """
253
    # TODO : change vente to purchase
254
    purchases_objects = Vente.objects.all().filter(facture=facture)
255 256 257
    # Get the article list and build an list out of it
    # contiaining (article_name, article_price, quantity, total_price)
    purchases_info = []
258
    for purchase in purchases_objects:
259 260 261 262 263 264
        purchases_info.append({
            'name': purchase.name,
            'price': purchase.prix,
            'quantity': purchase.number,
            'total_price': purchase.prix_total
        })
265
    return render_invoice(request, {
266 267 268
        'paid': True,
        'fid': facture.id,
        'DATE': facture.date,
269 270 271 272
        'recipient_name': "{} {}".format(
            facture.user.name,
            facture.user.surname
        ),
Maël Kervella's avatar
Maël Kervella committed
273
        'address': facture.user.room,
274
        'article': purchases_info,
275
        'total': facture.prix_total(),
276 277 278 279 280 281
        'asso_name': AssoOption.get_cached_value('name'),
        'line1': AssoOption.get_cached_value('adresse1'),
        'line2': AssoOption.get_cached_value('adresse2'),
        'siret': AssoOption.get_cached_value('siret'),
        'email': AssoOption.get_cached_value('contact'),
        'phone': AssoOption.get_cached_value('telephone'),
282
        'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
283
    })
284

Dalahro's avatar
Dalahro committed
285

286
# TODO : change facture to invoice
Dalahro's avatar
Dalahro committed
287
@login_required
288
@can_edit(Facture)
289
def edit_facture(request, facture, **_kwargs):
290 291 292 293 294 295
    """
    View used to edit an existing invoice.
    Articles can be added or remove to the invoice and quantity
    can be set as desired. This is also the view used to invalidate
    an invoice.
    """
296
    invoice_form = FactureForm(
297 298 299 300
        request.POST or None,
        instance=facture,
        user=request.user
    )
301 302
    purchases_objects = Vente.objects.filter(facture=facture)
    purchase_form_set = modelformset_factory(
chirac's avatar
chirac committed
303 304 305
        Vente,
        fields=('name', 'number'),
        extra=0,
306
        max_num=len(purchases_objects)
307
    )
308 309 310 311
    purchase_form = purchase_form_set(
        request.POST or None,
        queryset=purchases_objects
    )
312 313 314 315
    if invoice_form.is_valid() and purchase_form.is_valid():
        if invoice_form.changed_data:
            invoice_form.save()
        purchase_form.save()
316 317 318 319
        messages.success(
            request,
            _("The invoice has been successfully edited.")
        )
320
        return redirect(reverse('cotisations:index'))
321
    return form({
322 323
        'factureform': invoice_form,
        'venteform': purchase_form
324
    }, 'cotisations/edit_facture.html', request)
325

326

327
# TODO : change facture to invoice
328
@login_required
329
@can_delete(Facture)
330
def del_facture(request, facture, **_kwargs):
331 332 333
    """
    View used to delete an existing invocie.
    """
334
    if request.method == "POST":
335
        facture.delete()
336 337
        messages.success(
            request,
338
            _("The invoice has been successfully deleted.")
339
        )
340
        return redirect(reverse('cotisations:index'))
341 342
    return form({
        'objet': facture,
343
        'objet_name': _("Invoice")
344
    }, 'cotisations/delete.html', request)
345

346

chirac's avatar
chirac committed
347
@login_required
348
@can_create(Article)
349
def add_article(request):
350 351
    """
    View used to add an article.
352

353 354 355 356 357 358
    .. note:: If a purchase has already been sold, the price are calculated
        once and for all. That means even if the price of an article is edited
        later, it won't change the invoice. That is really important to keep
        this behaviour in order not to modify all the past and already
        accepted invoices.
    """
359 360
    article = ArticleForm(request.POST or None)
    if article.is_valid():
361
        article.save()
362 363 364 365
        messages.success(
            request,
            _("The article has been successfully created.")
        )
366
        return redirect(reverse('cotisations:index-article'))
367 368
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
369 370
        'action_name': _("Add"),
        'title': _("New article")
371
    }, 'cotisations/facture.html', request)
372

373

chirac's avatar
chirac committed
374
@login_required
375
@can_edit(Article)
376
def edit_article(request, article_instance, **_kwargs):
377 378 379
    """
    View used to edit an article.
    """
380 381
    article = ArticleForm(request.POST or None, instance=article_instance)
    if article.is_valid():
382 383
        if article.changed_data:
            article.save()
384 385 386 387
            messages.success(
                request,
                _("The article has been successfully edited.")
            )
388
        return redirect(reverse('cotisations:index-article'))
389 390
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
391 392
        'action_name': _('Edit'),
        'title': _("Edit article")
393
    }, 'cotisations/facture.html', request)
394

395

chirac's avatar
chirac committed
396
@login_required
397 398
@can_delete_set(Article)
def del_article(request, instances):
399 400 401
    """
    View used to delete one of the articles.
    """
402
    article = DelArticleForm(request.POST or None, instances=instances)
403 404
    if article.is_valid():
        article_del = article.cleaned_data['articles']
405
        article_del.delete()
406 407 408 409
        messages.success(
            request,
            _("The article(s) have been successfully deleted.")
        )
410
        return redirect(reverse('cotisations:index-article'))
411 412
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
413 414
        'action_name': _("Delete"),
        'title': _("Delete article")
415
    }, 'cotisations/facture.html', request)
416

417

418
# TODO : change paiement to payment
chirac's avatar
chirac committed
419
@login_required
420
@can_create(Paiement)
421
def add_paiement(request):
422 423 424
    """
    View used to add a payment method.
    """
425 426 427 428 429 430 431 432
    payment = PaiementForm(request.POST or None, prefix='payment')
    payment_method = payment_method_factory(
        payment.instance,
        request.POST or None,
        prefix='payment_method'
    )
    if payment.is_valid() and payment_method.is_valid():
        payment = payment.save()
433
        payment_method.save(payment)
434 435 436 437
        messages.success(
            request,
            _("The payment method has been successfully created.")
        )
438
        return redirect(reverse('cotisations:index-paiement'))
439 440
    return form({
        'factureform': payment,
441
        'payment_method': payment_method,
442 443
        'action_name': _("Add"),
        'title': _("New payment method")
444
    }, 'cotisations/facture.html', request)
445

446

447
# TODO : chnage paiement to Payment
chirac's avatar
chirac committed
448
@login_required
449
@can_edit(Paiement)
450
def edit_paiement(request, paiement_instance, **_kwargs):
451 452 453
    """
    View used to edit a payment method.
    """
454 455 456 457 458 459 460 461
    payment = PaiementForm(
        request.POST or None,
        instance=paiement_instance,
        prefix="payment"
    )
    payment_method = payment_method_factory(
        paiement_instance,
        request.POST or None,
462 463
        prefix='payment_method',
        creation=False
464 465 466 467 468 469 470 471 472
    )

    if payment.is_valid() and payment_method.is_valid():
        payment.save()
        payment_method.save()
        messages.success(
            request,
            _("The payement method has been successfully edited.")
        )
473
        return redirect(reverse('cotisations:index-paiement'))
474 475
    return form({
        'factureform': payment,
476
        'payment_method': payment_method,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
477 478
        'action_name': _("Edit"),
        'title': _("Edit payment method")
479
    }, 'cotisations/facture.html', request)
480

481

482
# TODO : change paiement to payment
chirac's avatar
chirac committed
483
@login_required
484 485
@can_delete_set(Paiement)
def del_paiement(request, instances):
486 487 488 489 490 491 492
    """
    View used to delete a set of payment methods.
    """
    payment = DelPaiementForm(request.POST or None, instances=instances)
    if payment.is_valid():
        payment_dels = payment.cleaned_data['paiements']
        for payment_del in payment_dels:
493
            try:
494
                payment_del.delete()
495
                messages.success(
chirac's avatar
chirac committed
496
                    request,
497
                    _("The payment method %(method_name)s has been \
498
                    successfully deleted.") % {
499
                        'method_name': payment_del
500 501
                    }
                )
502
            except ProtectedError:
503 504
                messages.error(
                    request,
505
                    _("The payment method %(method_name)s can't be deleted \
506
                    because there are invoices using it.") % {
507
                        'method_name': payment_del
508
                    }
509
                )
510
        return redirect(reverse('cotisations:index-paiement'))
511 512
    return form({
        'factureform': payment,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
513 514
        'action_name': _("Delete"),
        'title': _("Delete payment method")
515
    }, 'cotisations/facture.html', request)
516

517

518
# TODO : change banque to bank
chirac's avatar
chirac committed
519
@login_required
520
@can_create(Banque)
chirac's avatar
chirac committed
521
def add_banque(request):
522 523 524 525 526 527
    """
    View used to add a bank.
    """
    bank = BanqueForm(request.POST or None)
    if bank.is_valid():
        bank.save()
528 529 530 531
        messages.success(
            request,
            _("The bank has been successfully created.")
        )
532
        return redirect(reverse('cotisations:index-banque'))
533 534
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
535 536
        'action_name': _("Add"),
        'title': _("New bank")
537
    }, 'cotisations/facture.html', request)
538

539

540
# TODO : change banque to bank
chirac's avatar
chirac committed
541
@login_required
542
@can_edit(Banque)
543
def edit_banque(request, banque_instance, **_kwargs):
544 545 546 547 548 549 550
    """
    View used to edit a bank.
    """
    bank = BanqueForm(request.POST or None, instance=banque_instance)
    if bank.is_valid():
        if bank.changed_data:
            bank.save()
551 552 553 554
            messages.success(
                request,
                _("The bank has been successfully edited")
            )
555
        return redirect(reverse('cotisations:index-banque'))
556 557
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
558 559
        'action_name': _("Edit"),
        'title': _("Edit bank")
560
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
561

562

563
# TODO : chnage banque to bank
chirac's avatar
chirac committed
564
@login_required
565 566
@can_delete_set(Banque)
def del_banque(request, instances):
567 568 569 570 571 572 573
    """
    View used to delete a set of banks.
    """
    bank = DelBanqueForm(request.POST or None, instances=instances)
    if bank.is_valid():
        bank_dels = bank.cleaned_data['banques']
        for bank_del in bank_dels:
chirac's avatar
chirac committed
574
            try:
575
                bank_del.delete()
576 577 578 579
                messages.success(
                    request,
                    _("The bank %(bank_name)s has been successfully \
                    deleted.") % {
580
                        'bank_name': bank_del
581 582
                    }
                )
chirac's avatar
chirac committed
583
            except ProtectedError:
584 585 586 587
                messages.error(
                    request,
                    _("The bank %(bank_name)s can't be deleted \
                    because there are invoices using it.") % {
588
                        'bank_name': bank_del
589 590
                    }
                )
591
        return redirect(reverse('cotisations:index-banque'))
592 593
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
594 595
        'action_name': _("Delete"),
        'title': _("Delete bank")
596
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
597

598

599
# TODO : change facture to invoice
600
@login_required
601
@can_view_all(Facture)
602
@can_change(Facture, 'control')
603
def control(request):
604 605 606
    """
    View used to control the invoices all at once.
    """
607
    pagination_number = GeneralOption.get_cached_value('pagination_number')
608 609
    invoice_list = (Facture.objects.select_related('user').
                    select_related('paiement'))
610 611
    invoice_list = SortTable.sort(
        invoice_list,
612 613 614 615
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_CONTROL
    )
616
    control_invoices_formset = modelformset_factory(
chirac's avatar
chirac committed
617 618 619
        Facture,
        fields=('control', 'valid'),
        extra=0
620 621 622 623 624 625 626 627
    )
    invoice_list = re2o_paginator(request, invoice_list, pagination_number)
    control_invoices_form = control_invoices_formset(
        request.POST or None,
        queryset=invoice_list.object_list
    )
    if control_invoices_form.is_valid():
        control_invoices_form.save()
628
        reversion.set_comment("Controle")
629 630 631 632
        messages.success(
            request,
            _("Your changes have been properly taken into account.")
        )
633
        return redirect(reverse('cotisations:control'))
634
    return render(request, 'cotisations/control.html', {
635 636
        'facture_list': invoice_list,
        'controlform': control_invoices_form
637
    })
638

639

chirac's avatar
chirac committed
640
@login_required
641
@can_view_all(Article)
642
def index_article(request):
643 644 645 646
    """
    View used to display the list of all available articles.
    """
    # TODO : Offer other means of sorting
647
    article_list = Article.objects.order_by('name')
648 649
    return render(request, 'cotisations/index_article.html', {
        'article_list': article_list
650
    })
651

652

653
# TODO : change paiement to payment
chirac's avatar
chirac committed
654
@login_required
655
@can_view_all(Paiement)
656
def index_paiement(request):
657 658 659 660
    """
    View used to display the list of all available payment methods.
    """
    payment_list = Paiement.objects.order_by('moyen')
661
    return render(request, 'cotisations/index_paiement.html', {
662
        'paiement_list': payment_list
663
    })
664

665

666
# TODO : change banque to bank
chirac's avatar
chirac committed
667
@login_required
668
@can_view_all(Banque)
669
def index_banque(request):
670 671 672 673
    """
    View used to display the list of all available banks.
    """
    bank_list = Banque.objects.order_by('name')
674
    return render(request, 'cotisations/index_banque.html', {
675
        'banque_list': bank_list
676
    })
677

678

chirac's avatar
chirac committed
679
@login_required
680
@can_view_all(Facture)
681
def index(request):
682 683 684
    """
    View used to display the list of all exisitng invoices.
    """
685
    pagination_number = GeneralOption.get_cached_value('pagination_number')
686
    invoice_list = Facture.objects.select_related('user')\
687
        .select_related('paiement').prefetch_related('vente_set')
688 689
    invoice_list = SortTable.sort(
        invoice_list,
690 691 692 693
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_INDEX
    )
694
    invoice_list = re2o_paginator(request, invoice_list, pagination_number)
695
    return render(request, 'cotisations/index.html', {
696
        'facture_list': invoice_list
697
    })
698 699


700
# TODO : change solde to balance
701
@login_required
702 703
@can_edit(User)
def credit_solde(request, user, **_kwargs):
704
    """
705 706
    View used to edit the balance of a user.
    Can be use either to increase or decrease a user's balance.
707
    """
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
    try:
        balance = find_payment_method(Paiement.objects.get(is_balance=True))
    except Paiement.DoesNotExist:
        credit_allowed = False
    else:
        credit_allowed = (
            balance is not None
            and balance.can_credit_balance(request.user)
        )
    if not credit_allowed:
        messages.error(
            request,
            _("You are not allowed to credit your balance.")
        )
        return redirect(reverse(
            'users:profil',
            kwargs={'userid': user.id}
        ))

727 728
    refill_form = RechargeForm(request.POST or None, user=request.user)
    if refill_form.is_valid():
729
        price = refill_form.cleaned_data['value']
730
        invoice = Facture(user=user)
731
        invoice.paiement = refill_form.cleaned_data['payment']
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
        p = find_payment_method(invoice.paiement)
        if hasattr(p, 'check_price'):
            price_ok, msg = p.check_price(price, user)
            refill_form.add_error(None, msg)
        else:
            price_ok = True
        if price_ok:
            invoice.valid = True
            invoice.save()
            Vente.objects.create(
                facture=invoice,
                name='solde',
                prix=refill_form.cleaned_data['value'],
                number=1
            )
            return invoice.paiement.end_payment(invoice, request)
748
    p = get_object_or_404(Paiement, is_balance=True)
749
    return form({
750 751 752
        'factureform': refill_form,
        'balance': request.user.solde,
        'title': _("Refill your balance"),
753 754
        'action_name': _("Pay"),
        'max_balance': p.payment_method.maximum_balance,
755
    }, 'cotisations/facture.html', request)