Ce serveur Gitlab sera éteint le 30 juin 2020, pensez à migrer vos projets vers les serveurs gitlab-research.centralesupelec.fr et gitlab-student.centralesupelec.fr !

views.py 27.1 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
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
61 62 63 64 65 66 67 68 69
from .models import (
    Facture,
    Article,
    Vente,
    Paiement,
    Banque,
    CustomInvoice,
    BaseInvoice
)
70
from .forms import (
71
    FactureForm,
72 73 74 75 76 77
    ArticleForm,
    DelArticleForm,
    PaiementForm,
    DelPaiementForm,
    BanqueForm,
    DelBanqueForm,
78
    SelectArticleForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
79 80
    RechargeForm,
    CustomInvoiceForm
81
)
82
from .tex import render_invoice
83
from .payment_methods.forms import payment_method_factory
84
from .utils import find_payment_method, send_mail_invoice
85

86

chirac's avatar
chirac committed
87
@login_required
88 89 90
@can_create(Facture)
@can_edit(User)
def new_facture(request, user, userid):
91 92 93
    """
    View called to create a new invoice.
    Currently, Send the list of available articles for the user along with
94
    a formset of a new invoice (based on the `:forms:FactureForm()` form.
95 96 97 98 99 100
    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)
101 102 103
    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
104
    # Building the invoice form and the article formset
105
    invoice_form = FactureForm(
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
106 107
        request.POST or None,
        instance=invoice,
108 109
        user=request.user,
        creation=True
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
110
    )
111

112 113 114 115
    article_formset = formset_factory(SelectArticleForm)(
        request.POST or None,
        form_kwargs={'user': request.user, 'target_user': user}
    )
116 117 118

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

151
                send_mail_invoice(new_invoice_instance)
152

153 154 155 156 157 158 159 160
                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
161
            )
162 163 164 165 166
    p = Paiement.objects.filter(is_balance=True)
    if len(p) and p[0].can_use_payment(request.user):
        balance = user.solde
    else:
        balance = None
167

168 169
    return form(
        {
170
            'factureform': invoice_form,
171 172 173 174
            'articlesformset': article_formset,
            'articlelist': article_list,
            'balance': balance,
            'action_name': _('Create'),
175
        },
176
        'cotisations/facture.html', request
177
    )
178

179

180
# TODO : change facture to invoice
chirac's avatar
chirac committed
181
@login_required
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
182 183
@can_create(CustomInvoice)
def new_custom_invoice(request):
184
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
185
    View used to generate a custom invoice. It's mainly used to
186 187 188
    get invoices that are not taken into account, for the administrative
    point of view.
    """
189 190 191 192 193
    # 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
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
194
    invoice_form = CustomInvoiceForm(request.POST or None)
195 196 197 198 199 200

    article_formset = formset_factory(SelectArticleForm)(
        request.POST or None,
        form_kwargs={'user': request.user, 'target_user': user}
    )

201
    if invoice_form.is_valid() and articles_formset.is_valid():
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        new_invoice_instance = invoice_form.save()
        for art_item in articles_formset:
            if art_item.cleaned_data:
                article = art_item.cleaned_data['article']
                quantity = art_item.cleaned_data['quantity']
                Vente.objects.create(
                    facture=new_invoice_instance,
                    name=article.name,
                    prix=article.prix,
                    type_cotisation=article.type_cotisation,
                    duration=article.duration,
                    number=quantity
                )
        messages.success(
            request,
217
            _("The custom invoice was created.")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
218 219 220 221
        )
        return redirect(reverse('cotisations:index-custom-invoice'))


222
    return form({
223
        'factureform': invoice_form,
224 225
        'action_name': _("Create"),
        'articlesformset': articles_formset,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
226
        'articlelist': articles
227
    }, 'cotisations/facture.html', request)
228

229

230
# TODO : change facture to invoice
Dalahro's avatar
Dalahro committed
231
@login_required
232
@can_view(Facture)
233
def facture_pdf(request, facture, **_kwargs):
234 235 236 237 238 239
    """
    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.
    """
240
    # TODO : change vente to purchase
241
    purchases_objects = Vente.objects.all().filter(facture=facture)
242 243 244
    # Get the article list and build an list out of it
    # contiaining (article_name, article_price, quantity, total_price)
    purchases_info = []
245
    for purchase in purchases_objects:
246 247 248 249 250 251
        purchases_info.append({
            'name': purchase.name,
            'price': purchase.prix,
            'quantity': purchase.number,
            'total_price': purchase.prix_total
        })
252
    return render_invoice(request, {
253 254 255
        'paid': True,
        'fid': facture.id,
        'DATE': facture.date,
256 257 258 259
        'recipient_name': "{} {}".format(
            facture.user.name,
            facture.user.surname
        ),
Maël Kervella's avatar
Maël Kervella committed
260
        'address': facture.user.room,
261
        'article': purchases_info,
262
        'total': facture.prix_total(),
263 264 265 266 267 268
        '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'),
269
        'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
270
    })
271

Dalahro's avatar
Dalahro committed
272

273
# TODO : change facture to invoice
Dalahro's avatar
Dalahro committed
274
@login_required
275
@can_edit(Facture)
276
def edit_facture(request, facture, **_kwargs):
277 278
    """
    View used to edit an existing invoice.
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
279
    Articles can be added or removed to the invoice and quantity
280 281 282
    can be set as desired. This is also the view used to invalidate
    an invoice.
    """
283
    invoice_form = FactureForm(
284 285 286 287
        request.POST or None,
        instance=facture,
        user=request.user
    )
288 289
    purchases_objects = Vente.objects.filter(facture=facture)
    purchase_form_set = modelformset_factory(
chirac's avatar
chirac committed
290 291 292
        Vente,
        fields=('name', 'number'),
        extra=0,
293
        max_num=len(purchases_objects)
294
    )
295 296 297 298
    purchase_form = purchase_form_set(
        request.POST or None,
        queryset=purchases_objects
    )
299 300 301 302
    if invoice_form.is_valid() and purchase_form.is_valid():
        if invoice_form.changed_data:
            invoice_form.save()
        purchase_form.save()
303 304
        messages.success(
            request,
305
            _("The invoice was edited.")
306
        )
307
        return redirect(reverse('cotisations:index'))
308
    return form({
309 310
        'factureform': invoice_form,
        'venteform': purchase_form
311
    }, 'cotisations/edit_facture.html', request)
312

313

314
# TODO : change facture to invoice
315
@login_required
316
@can_delete(Facture)
317
def del_facture(request, facture, **_kwargs):
318 319 320
    """
    View used to delete an existing invocie.
    """
321
    if request.method == "POST":
322
        facture.delete()
323 324
        messages.success(
            request,
325
            _("The invoice was deleted.")
326
        )
327
        return redirect(reverse('cotisations:index'))
328 329
    return form({
        'objet': facture,
330
        'objet_name': _("Invoice")
331
    }, 'cotisations/delete.html', request)
332

333

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
@login_required
@can_edit(CustomInvoice)
def edit_custom_invoice(request, invoice, **kwargs):
    # Building the invocie form and the article formset
    invoice_form = CustomInvoiceForm(
        request.POST or None,
        instance=invoice
    )
    purchases_objects = Vente.objects.filter(facture=invoice)
    purchase_form_set = modelformset_factory(
        Vente,
        fields=('name', 'number'),
        extra=0,
        max_num=len(purchases_objects)
    )
    purchase_form = purchase_form_set(
        request.POST or None,
        queryset=purchases_objects
    )
    if invoice_form.is_valid() and purchase_form.is_valid():
        if invoice_form.changed_data:
            invoice_form.save()
        purchase_form.save()
        messages.success(
            request,
359
            _("The invoice was edited.")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
        )
        return redirect(reverse('cotisations:index-custom-invoice'))

    return form({
        'factureform': invoice_form,
        'venteform': purchase_form
    }, 'cotisations/edit_facture.html', request)


@login_required
@can_view(CustomInvoice)
def custom_invoice_pdf(request, invoice, **_kwargs):
    """
    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.
    """
    # TODO : change vente to purchase
    purchases_objects = Vente.objects.all().filter(facture=invoice)
    # Get the article list and build an list out of it
    # contiaining (article_name, article_price, quantity, total_price)
    purchases_info = []
    for purchase in purchases_objects:
        purchases_info.append({
            'name': purchase.name,
            'price': purchase.prix,
            'quantity': purchase.number,
            'total_price': purchase.prix_total
        })
    return render_invoice(request, {
        'paid': invoice.paid,
        'fid': invoice.id,
        'DATE': invoice.date,
        'recipient_name': invoice.recipient,
        'address': invoice.address,
        'article': purchases_info,
        'total': invoice.prix_total(),
        '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'),
        'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH)
    })


# TODO : change facture to invoice
@login_required
@can_delete(CustomInvoice)
def del_custom_invoice(request, invoice, **_kwargs):
    """
    View used to delete an existing invocie.
    """
    if request.method == "POST":
        invoice.delete()
        messages.success(
            request,
419
            _("The invoice was deleted.")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
420 421 422 423 424 425 426 427
        )
        return redirect(reverse('cotisations:index-custom-invoice'))
    return form({
        'objet': invoice,
        'objet_name': _("Invoice")
    }, 'cotisations/delete.html', request)


chirac's avatar
chirac committed
428
@login_required
429
@can_create(Article)
430
def add_article(request):
431 432
    """
    View used to add an article.
433

434 435 436 437 438 439
    .. 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.
    """
440 441
    article = ArticleForm(request.POST or None)
    if article.is_valid():
442
        article.save()
443 444
        messages.success(
            request,
445
            _("The article was created.")
446
        )
447
        return redirect(reverse('cotisations:index-article'))
448 449
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
450 451
        'action_name': _("Add"),
        'title': _("New article")
452
    }, 'cotisations/facture.html', request)
453

454

chirac's avatar
chirac committed
455
@login_required
456
@can_edit(Article)
457
def edit_article(request, article_instance, **_kwargs):
458 459 460
    """
    View used to edit an article.
    """
461 462
    article = ArticleForm(request.POST or None, instance=article_instance)
    if article.is_valid():
463 464
        if article.changed_data:
            article.save()
465 466
            messages.success(
                request,
467
                _("The article was edited.")
468
            )
469
        return redirect(reverse('cotisations:index-article'))
470 471
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
472 473
        'action_name': _('Edit'),
        'title': _("Edit article")
474
    }, 'cotisations/facture.html', request)
475

476

chirac's avatar
chirac committed
477
@login_required
478 479
@can_delete_set(Article)
def del_article(request, instances):
480 481 482
    """
    View used to delete one of the articles.
    """
483
    article = DelArticleForm(request.POST or None, instances=instances)
484 485
    if article.is_valid():
        article_del = article.cleaned_data['articles']
486
        article_del.delete()
487 488
        messages.success(
            request,
489
            _("The articles were deleted.")
490
        )
491
        return redirect(reverse('cotisations:index-article'))
492 493
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
494 495
        'action_name': _("Delete"),
        'title': _("Delete article")
496
    }, 'cotisations/facture.html', request)
497

498

499
# TODO : change paiement to payment
chirac's avatar
chirac committed
500
@login_required
501
@can_create(Paiement)
502
def add_paiement(request):
503 504 505
    """
    View used to add a payment method.
    """
506 507 508 509 510 511 512 513
    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()
514
        payment_method.save(payment)
515 516
        messages.success(
            request,
517
            _("The payment method was created.")
518
        )
519
        return redirect(reverse('cotisations:index-paiement'))
520 521
    return form({
        'factureform': payment,
522
        'payment_method': payment_method,
523 524
        'action_name': _("Add"),
        'title': _("New payment method")
525
    }, 'cotisations/facture.html', request)
526

527

528
# TODO : chnage paiement to Payment
chirac's avatar
chirac committed
529
@login_required
530
@can_edit(Paiement)
531
def edit_paiement(request, paiement_instance, **_kwargs):
532 533 534
    """
    View used to edit a payment method.
    """
535 536 537 538 539 540 541 542
    payment = PaiementForm(
        request.POST or None,
        instance=paiement_instance,
        prefix="payment"
    )
    payment_method = payment_method_factory(
        paiement_instance,
        request.POST or None,
543 544
        prefix='payment_method',
        creation=False
545 546
    )

547 548
    if payment.is_valid() and \
       (payment_method is None or payment_method.is_valid()):
549
        payment.save()
550 551
        if payment_method is not None:
            payment_method.save()
552
        messages.success(
553
            request,_("The payment method was edited.")
554
        )
555
        return redirect(reverse('cotisations:index-paiement'))
556 557
    return form({
        'factureform': payment,
558
        'payment_method': payment_method,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
559 560
        'action_name': _("Edit"),
        'title': _("Edit payment method")
561
    }, 'cotisations/facture.html', request)
562

563

564
# TODO : change paiement to payment
chirac's avatar
chirac committed
565
@login_required
566 567
@can_delete_set(Paiement)
def del_paiement(request, instances):
568 569 570 571 572 573 574
    """
    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:
575
            try:
576
                payment_del.delete()
577
                messages.success(
chirac's avatar
chirac committed
578
                    request,
579
                    _("The payment method %(method_name)s was deleted.") % {
580
                        'method_name': payment_del
581 582
                    }
                )
583
            except ProtectedError:
584 585
                messages.error(
                    request,
586
                    _("The payment method %(method_name)s can't be deleted \
587
                    because there are invoices using it.") % {
588
                        'method_name': payment_del
589
                    }
590
                )
591
        return redirect(reverse('cotisations:index-paiement'))
592 593
    return form({
        'factureform': payment,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
594 595
        'action_name': _("Delete"),
        'title': _("Delete payment method")
596
    }, 'cotisations/facture.html', request)
597

598

599
# TODO : change banque to bank
chirac's avatar
chirac committed
600
@login_required
601
@can_create(Banque)
chirac's avatar
chirac committed
602
def add_banque(request):
603 604 605 606 607 608
    """
    View used to add a bank.
    """
    bank = BanqueForm(request.POST or None)
    if bank.is_valid():
        bank.save()
609 610
        messages.success(
            request,
611
            _("The bank was created.")
612
        )
613
        return redirect(reverse('cotisations:index-banque'))
614 615
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
616 617
        'action_name': _("Add"),
        'title': _("New bank")
618
    }, 'cotisations/facture.html', request)
619

620

621
# TODO : change banque to bank
chirac's avatar
chirac committed
622
@login_required
623
@can_edit(Banque)
624
def edit_banque(request, banque_instance, **_kwargs):
625 626 627 628 629 630 631
    """
    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()
632 633
            messages.success(
                request,
634
                _("The bank was edited.")
635
            )
636
        return redirect(reverse('cotisations:index-banque'))
637 638
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
639 640
        'action_name': _("Edit"),
        'title': _("Edit bank")
641
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
642

643

644
# TODO : chnage banque to bank
chirac's avatar
chirac committed
645
@login_required
646 647
@can_delete_set(Banque)
def del_banque(request, instances):
648 649 650 651 652 653 654
    """
    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
655
            try:
656
                bank_del.delete()
657 658
                messages.success(
                    request,
659
                    _("The bank %(bank_name)s was deleted.") % {
660
                        'bank_name': bank_del
661 662
                    }
                )
chirac's avatar
chirac committed
663
            except ProtectedError:
664 665 666 667
                messages.error(
                    request,
                    _("The bank %(bank_name)s can't be deleted \
                    because there are invoices using it.") % {
668
                        'bank_name': bank_del
669 670
                    }
                )
671
        return redirect(reverse('cotisations:index-banque'))
672 673
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
674 675
        'action_name': _("Delete"),
        'title': _("Delete bank")
676
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
677

678

679
# TODO : change facture to invoice
680
@login_required
681
@can_view_all(Facture)
682
@can_change(Facture, 'control')
683
def control(request):
684 685 686
    """
    View used to control the invoices all at once.
    """
687
    pagination_number = GeneralOption.get_cached_value('pagination_number')
688 689
    invoice_list = (Facture.objects.select_related('user').
                    select_related('paiement'))
690 691
    invoice_list = SortTable.sort(
        invoice_list,
692 693 694 695
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_CONTROL
    )
696
    control_invoices_formset = modelformset_factory(
chirac's avatar
chirac committed
697 698 699
        Facture,
        fields=('control', 'valid'),
        extra=0
700 701 702 703 704 705 706 707
    )
    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()
708
        reversion.set_comment("Controle")
709 710 711 712
        messages.success(
            request,
            _("Your changes have been properly taken into account.")
        )
713
        return redirect(reverse('cotisations:control'))
714
    return render(request, 'cotisations/control.html', {
715 716
        'facture_list': invoice_list,
        'controlform': control_invoices_form
717
    })
718

719

chirac's avatar
chirac committed
720
@login_required
721
@can_view_all(Article)
722
def index_article(request):
723 724 725 726
    """
    View used to display the list of all available articles.
    """
    # TODO : Offer other means of sorting
727
    article_list = Article.objects.order_by('name')
728 729
    return render(request, 'cotisations/index_article.html', {
        'article_list': article_list
730
    })
731

732

733
# TODO : change paiement to payment
chirac's avatar
chirac committed
734
@login_required
735
@can_view_all(Paiement)
736
def index_paiement(request):
737 738 739 740
    """
    View used to display the list of all available payment methods.
    """
    payment_list = Paiement.objects.order_by('moyen')
741
    return render(request, 'cotisations/index_paiement.html', {
742
        'paiement_list': payment_list
743
    })
744

745

746
# TODO : change banque to bank
chirac's avatar
chirac committed
747
@login_required
748
@can_view_all(Banque)
749
def index_banque(request):
750 751 752 753
    """
    View used to display the list of all available banks.
    """
    bank_list = Banque.objects.order_by('name')
754
    return render(request, 'cotisations/index_banque.html', {
755
        'banque_list': bank_list
756
    })
757

758

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
@login_required
@can_view_all(CustomInvoice)
def index_custom_invoice(request):
    """View used to display every custom invoice."""
    pagination_number = GeneralOption.get_cached_value('pagination_number')
    custom_invoice_list = CustomInvoice.objects.prefetch_related('vente_set')
    custom_invoice_list = SortTable.sort(
        custom_invoice_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_CUSTOM
    )
    custom_invoice_list = re2o_paginator(
        request,
        custom_invoice_list,
        pagination_number,
    )
    return render(request, 'cotisations/index_custom_invoice.html', {
        'custom_invoice_list': custom_invoice_list
    })


chirac's avatar
chirac committed
781
@login_required
782
@can_view_all(Facture)
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
783
@can_view_all(CustomInvoice)
784
def index(request):
785 786 787
    """
    View used to display the list of all exisitng invoices.
    """
788
    pagination_number = GeneralOption.get_cached_value('pagination_number')
789
    invoice_list = Facture.objects.select_related('user')\
790
        .select_related('paiement').prefetch_related('vente_set')
791 792
    invoice_list = SortTable.sort(
        invoice_list,
793 794 795 796
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_INDEX
    )
797
    invoice_list = re2o_paginator(request, invoice_list, pagination_number)
798
    return render(request, 'cotisations/index.html', {
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
799
        'facture_list': invoice_list,
800
    })
801 802


803
# TODO : change solde to balance
804
@login_required
805 806
@can_edit(User)
def credit_solde(request, user, **_kwargs):
807
    """
808 809
    View used to edit the balance of a user.
    Can be use either to increase or decrease a user's balance.
810
    """
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
    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}
        ))

830
    refill_form = RechargeForm(request.POST or None, user=user, user_source=request.user)
831
    if refill_form.is_valid():
832
        price = refill_form.cleaned_data['value']
833
        invoice = Facture(user=user)
834
        invoice.paiement = refill_form.cleaned_data['payment']
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
        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
            )
850 851 852

            send_mail_invoice(invoice)

853
            return invoice.paiement.end_payment(invoice, request)
854
    p = get_object_or_404(Paiement, is_balance=True)
855
    return form({
856
        'factureform': refill_form,
857
        'balance': user.solde,
858
        'title': _("Refill your balance"),
859 860
        'action_name': _("Pay"),
        'max_balance': p.payment_method.maximum_balance,
861
    }, 'cotisations/facture.html', request)
862