views.py 27.8 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 78 79
    ArticleForm,
    DelArticleForm,
    PaiementForm,
    DelPaiementForm,
    BanqueForm,
    DelBanqueForm,
    SelectUserArticleForm,
    SelectClubArticleForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
80 81
    RechargeForm,
    CustomInvoiceForm
82
)
83
from .tex import render_invoice
84
from .payment_methods.forms import payment_method_factory
85
from .utils import find_payment_method, send_mail_invoice
86

87

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

113
    if request.user.is_class_club:
114
        article_formset = formset_factory(SelectClubArticleForm)(
115
            request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
116
            form_kwargs={'user': request.user}
117
        )
118
    else:
119
        article_formset = formset_factory(SelectUserArticleForm)(
120
            request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
121
            form_kwargs={'user': request.user}
122
        )
123 124 125

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

158
                send_mail_invoice(new_invoice_instance)
159

160 161 162 163 164 165 166 167
                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
168
            )
169 170 171 172 173
    p = Paiement.objects.filter(is_balance=True)
    if len(p) and p[0].can_use_payment(request.user):
        balance = user.solde
    else:
        balance = None
174

175 176
    return form(
        {
177
            'factureform': invoice_form,
178 179 180 181
            'articlesformset': article_formset,
            'articlelist': article_list,
            'balance': balance,
            'action_name': _('Create'),
182
        },
183
        'cotisations/facture.html', request
184
    )
185

186

187
# TODO : change facture to invoice
chirac's avatar
chirac committed
188
@login_required
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
189 190
@can_create(CustomInvoice)
def new_custom_invoice(request):
191
    """
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
192
    View used to generate a custom invoice. It's mainly used to
193 194 195
    get invoices that are not taken into account, for the administrative
    point of view.
    """
196 197 198 199 200
    # 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
201
    invoice_form = CustomInvoiceForm(request.POST or None)
202
    if request.user.is_class_club:
203
        articles_formset = formset_factory(SelectClubArticleForm)(
204 205
            request.POST or None,
            form_kwargs={'user': request.user}
206
        )
207
    else:
208
        articles_formset = formset_factory(SelectUserArticleForm)(
209 210
            request.POST or None,
            form_kwargs={'user': request.user}
211
        )
212
    if invoice_form.is_valid() and articles_formset.is_valid():
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
        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,
            _('The custom invoice was successfully created.')
        )
        return redirect(reverse('cotisations:index-custom-invoice'))


233
    return form({
234
        'factureform': invoice_form,
235 236
        'action_name': _("Create"),
        'articlesformset': articles_formset,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
237
        'articlelist': articles
238
    }, 'cotisations/facture.html', request)
239

240

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

Dalahro's avatar
Dalahro committed
283

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

324

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

344

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 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 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
@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,
            _("The invoice has been successfully edited.")
        )
        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,
            _("The invoice has been successfully deleted.")
        )
        return redirect(reverse('cotisations:index-custom-invoice'))
    return form({
        'objet': invoice,
        'objet_name': _("Invoice")
    }, 'cotisations/delete.html', request)


chirac's avatar
chirac committed
439
@login_required
440
@can_create(Article)
441
def add_article(request):
442 443
    """
    View used to add an article.
444

445 446 447 448 449 450
    .. 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.
    """
451 452
    article = ArticleForm(request.POST or None)
    if article.is_valid():
453
        article.save()
454 455 456 457
        messages.success(
            request,
            _("The article has been successfully created.")
        )
458
        return redirect(reverse('cotisations:index-article'))
459 460
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
461 462
        'action_name': _("Add"),
        'title': _("New article")
463
    }, 'cotisations/facture.html', request)
464

465

chirac's avatar
chirac committed
466
@login_required
467
@can_edit(Article)
468
def edit_article(request, article_instance, **_kwargs):
469 470 471
    """
    View used to edit an article.
    """
472 473
    article = ArticleForm(request.POST or None, instance=article_instance)
    if article.is_valid():
474 475
        if article.changed_data:
            article.save()
476 477 478 479
            messages.success(
                request,
                _("The article has been successfully edited.")
            )
480
        return redirect(reverse('cotisations:index-article'))
481 482
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
483 484
        'action_name': _('Edit'),
        'title': _("Edit article")
485
    }, 'cotisations/facture.html', request)
486

487

chirac's avatar
chirac committed
488
@login_required
489 490
@can_delete_set(Article)
def del_article(request, instances):
491 492 493
    """
    View used to delete one of the articles.
    """
494
    article = DelArticleForm(request.POST or None, instances=instances)
495 496
    if article.is_valid():
        article_del = article.cleaned_data['articles']
497
        article_del.delete()
498 499 500 501
        messages.success(
            request,
            _("The article(s) have been successfully deleted.")
        )
502
        return redirect(reverse('cotisations:index-article'))
503 504
    return form({
        'factureform': article,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
505 506
        'action_name': _("Delete"),
        'title': _("Delete article")
507
    }, 'cotisations/facture.html', request)
508

509

510
# TODO : change paiement to payment
chirac's avatar
chirac committed
511
@login_required
512
@can_create(Paiement)
513
def add_paiement(request):
514 515 516
    """
    View used to add a payment method.
    """
517 518 519 520 521 522 523 524
    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()
525
        payment_method.save(payment)
526 527 528 529
        messages.success(
            request,
            _("The payment method has been successfully created.")
        )
530
        return redirect(reverse('cotisations:index-paiement'))
531 532
    return form({
        'factureform': payment,
533
        'payment_method': payment_method,
534 535
        'action_name': _("Add"),
        'title': _("New payment method")
536
    }, 'cotisations/facture.html', request)
537

538

539
# TODO : chnage paiement to Payment
chirac's avatar
chirac committed
540
@login_required
541
@can_edit(Paiement)
542
def edit_paiement(request, paiement_instance, **_kwargs):
543 544 545
    """
    View used to edit a payment method.
    """
546 547 548 549 550 551 552 553
    payment = PaiementForm(
        request.POST or None,
        instance=paiement_instance,
        prefix="payment"
    )
    payment_method = payment_method_factory(
        paiement_instance,
        request.POST or None,
554 555
        prefix='payment_method',
        creation=False
556 557
    )

558 559
    if payment.is_valid() and \
       (payment_method is None or payment_method.is_valid()):
560
        payment.save()
561 562
        if payment_method is not None:
            payment_method.save()
563 564 565 566
        messages.success(
            request,
            _("The payement method has been successfully edited.")
        )
567
        return redirect(reverse('cotisations:index-paiement'))
568 569
    return form({
        'factureform': payment,
570
        'payment_method': payment_method,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
571 572
        'action_name': _("Edit"),
        'title': _("Edit payment method")
573
    }, 'cotisations/facture.html', request)
574

575

576
# TODO : change paiement to payment
chirac's avatar
chirac committed
577
@login_required
578 579
@can_delete_set(Paiement)
def del_paiement(request, instances):
580 581 582 583 584 585 586
    """
    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:
587
            try:
588
                payment_del.delete()
589
                messages.success(
chirac's avatar
chirac committed
590
                    request,
591
                    _("The payment method %(method_name)s has been \
592
                    successfully deleted.") % {
593
                        'method_name': payment_del
594 595
                    }
                )
596
            except ProtectedError:
597 598
                messages.error(
                    request,
599
                    _("The payment method %(method_name)s can't be deleted \
600
                    because there are invoices using it.") % {
601
                        'method_name': payment_del
602
                    }
603
                )
604
        return redirect(reverse('cotisations:index-paiement'))
605 606
    return form({
        'factureform': payment,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
607 608
        'action_name': _("Delete"),
        'title': _("Delete payment method")
609
    }, 'cotisations/facture.html', request)
610

611

612
# TODO : change banque to bank
chirac's avatar
chirac committed
613
@login_required
614
@can_create(Banque)
chirac's avatar
chirac committed
615
def add_banque(request):
616 617 618 619 620 621
    """
    View used to add a bank.
    """
    bank = BanqueForm(request.POST or None)
    if bank.is_valid():
        bank.save()
622 623 624 625
        messages.success(
            request,
            _("The bank has been successfully created.")
        )
626
        return redirect(reverse('cotisations:index-banque'))
627 628
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
629 630
        'action_name': _("Add"),
        'title': _("New bank")
631
    }, 'cotisations/facture.html', request)
632

633

634
# TODO : change banque to bank
chirac's avatar
chirac committed
635
@login_required
636
@can_edit(Banque)
637
def edit_banque(request, banque_instance, **_kwargs):
638 639 640 641 642 643 644
    """
    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()
645 646 647 648
            messages.success(
                request,
                _("The bank has been successfully edited")
            )
649
        return redirect(reverse('cotisations:index-banque'))
650 651
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
652 653
        'action_name': _("Edit"),
        'title': _("Edit bank")
654
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
655

656

657
# TODO : chnage banque to bank
chirac's avatar
chirac committed
658
@login_required
659 660
@can_delete_set(Banque)
def del_banque(request, instances):
661 662 663 664 665 666 667
    """
    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
668
            try:
669
                bank_del.delete()
670 671 672 673
                messages.success(
                    request,
                    _("The bank %(bank_name)s has been successfully \
                    deleted.") % {
674
                        'bank_name': bank_del
675 676
                    }
                )
chirac's avatar
chirac committed
677
            except ProtectedError:
678 679 680 681
                messages.error(
                    request,
                    _("The bank %(bank_name)s can't be deleted \
                    because there are invoices using it.") % {
682
                        'bank_name': bank_del
683 684
                    }
                )
685
        return redirect(reverse('cotisations:index-banque'))
686 687
    return form({
        'factureform': bank,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
688 689
        'action_name': _("Delete"),
        'title': _("Delete bank")
690
    }, 'cotisations/facture.html', request)
chirac's avatar
chirac committed
691

692

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

733

chirac's avatar
chirac committed
734
@login_required
735
@can_view_all(Article)
736
def index_article(request):
737 738 739 740
    """
    View used to display the list of all available articles.
    """
    # TODO : Offer other means of sorting
741
    article_list = Article.objects.order_by('name')
742 743
    return render(request, 'cotisations/index_article.html', {
        'article_list': article_list
744
    })
745

746

747
# TODO : change paiement to payment
chirac's avatar
chirac committed
748
@login_required
749
@can_view_all(Paiement)
750
def index_paiement(request):
751 752 753 754
    """
    View used to display the list of all available payment methods.
    """
    payment_list = Paiement.objects.order_by('moyen')
755
    return render(request, 'cotisations/index_paiement.html', {
756
        'paiement_list': payment_list
757
    })
758

759

760
# TODO : change banque to bank
chirac's avatar
chirac committed
761
@login_required
762
@can_view_all(Banque)
763
def index_banque(request):
764 765 766 767
    """
    View used to display the list of all available banks.
    """
    bank_list = Banque.objects.order_by('name')
768
    return render(request, 'cotisations/index_banque.html', {
769
        'banque_list': bank_list
770
    })
771

772

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
@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
795
@login_required
796
@can_view_all(Facture)
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
797
@can_view_all(CustomInvoice)
798
def index(request):
799 800 801
    """
    View used to display the list of all exisitng invoices.
    """
802
    pagination_number = GeneralOption.get_cached_value('pagination_number')
803
    invoice_list = Facture.objects.select_related('user')\
804
        .select_related('paiement').prefetch_related('vente_set')
805 806
    invoice_list = SortTable.sort(
        invoice_list,
807 808 809 810
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.COTISATIONS_INDEX
    )
811
    invoice_list = re2o_paginator(request, invoice_list, pagination_number)
812
    return render(request, 'cotisations/index.html', {
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
813
        'facture_list': invoice_list,
814
    })
815 816


817
# TODO : change solde to balance
818
@login_required
819 820
@can_edit(User)
def credit_solde(request, user, **_kwargs):
821
    """
822 823
    View used to edit the balance of a user.
    Can be use either to increase or decrease a user's balance.
824
    """
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
    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}
        ))

844 845
    refill_form = RechargeForm(request.POST or None, user=request.user)
    if refill_form.is_valid():
846
        price = refill_form.cleaned_data['value']
847
        invoice = Facture(user=user)
848
        invoice.paiement = refill_form.cleaned_data['payment']
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
        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
            )
864 865 866

            send_mail_invoice(invoice)

867
            return invoice.paiement.end_payment(invoice, request)
868
    p = get_object_or_404(Paiement, is_balance=True)
869
    return form({
870 871 872
        'factureform': refill_form,
        'balance': request.user.solde,
        'title': _("Refill your balance"),
873 874
        'action_name': _("Pay"),
        'max_balance': p.payment_method.maximum_balance,
875
    }, 'cotisations/facture.html', request)