8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-04 18:42:25 +00:00
re2o/cotisations/views.py

1066 lines
36 KiB
Python
Raw Permalink Normal View History

2020-11-23 16:06:37 +00:00
# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
2017-01-15 23:01:18 +00:00
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Lara Kermarec
2017-01-15 23:01:18 +00:00
# Copyright © 2017 Augustin Lemesle
# Copyright © 2018 Hugo Levy-Falk
2017-01-15 23:01:18 +00:00
#
# 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.
2016-07-02 13:58:50 +00:00
# App de gestion des users pour re2o
# Lara Kermarec, Gabriel Détraz
2016-07-02 13:58:50 +00:00
# Gplv2
2018-04-14 13:39:51 +00:00
"""cotisations.views
The different views used in the Cotisations module
"""
from __future__ import unicode_literals
2021-02-10 10:06:09 +00:00
2017-10-13 20:47:32 +00:00
import os
2017-10-26 22:11:18 +00:00
2016-07-02 13:58:50 +00:00
from django.contrib import messages
2021-02-10 10:06:09 +00:00
from django.contrib.auth.decorators import login_required
from django.db.models import ProtectedError, Q
from django.forms import formset_factory, modelformset_factory
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
2017-10-13 20:47:32 +00:00
from django.utils import timezone
from django.utils.translation import ugettext as _
2017-10-13 20:47:32 +00:00
# Import des models, forms et fonctions re2o
2018-04-03 01:47:03 +00:00
from reversion import revisions as reversion
2021-02-10 10:06:09 +00:00
from preferences.models import AssoOption, GeneralOption, Mandate
2017-10-13 20:47:32 +00:00
from re2o import settings
2021-02-10 10:06:09 +00:00
from re2o.acl import (can_change, can_create, can_delete, can_delete_set,
can_edit, can_view, can_view_all)
from re2o.base import SortTable, re2o_paginator
2021-02-10 10:06:09 +00:00
from re2o.settings import LOGO_PATH
from re2o.views import form
from users.models import User
from .forms import (ArticleForm, BanqueForm, CostEstimateForm,
CustomInvoiceForm, DelArticleForm, DelBanqueForm,
DelPaiementForm, DiscountForm, FactureForm, PaiementForm,
RechargeForm, SelectArticleForm)
from .models import (Article, Banque, BaseInvoice, CostEstimate, CustomInvoice,
Facture, Paiement, Vente)
from .payment_methods.forms import payment_method_factory
2021-02-10 10:06:09 +00:00
from .tex import escape_chars, render_invoice, render_voucher
from .utils import find_payment_method
2016-07-02 13:58:50 +00:00
2016-07-08 10:35:53 +00:00
@login_required
@can_create(Facture)
@can_edit(User)
def new_facture(request, user, userid):
"""
View called to create a new invoice.
Currently, Send the list of available articles for the user along with
2018-07-10 16:38:05 +00:00
a formset of a new invoice (based on the `:forms:FactureForm()` form.
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)
article_list = Article.objects.filter(
2019-12-27 16:00:20 +00:00
Q(type_user="All") | Q(type_user=user.class_type)
)
2018-07-03 16:53:44 +00:00
# Building the invoice form and the article formset
2018-07-10 16:38:05 +00:00
invoice_form = FactureForm(
request.POST or None, instance=invoice, user=request.user, creation=True
2018-07-03 16:53:44 +00:00
)
article_formset = formset_factory(SelectArticleForm)(
request.POST or None, form_kwargs={"user": request.user, "target_user": user}
)
if invoice_form.is_valid() and article_formset.is_valid():
new_invoice_instance = invoice_form.save(commit=False)
articles = article_formset
2019-09-30 09:33:01 +00:00
# Check if at least one article has been selected
if any(art.cleaned_data for art in articles):
# Building a purchase for each article sold
purchases = []
total_price = 0
for art_item in articles:
if art_item.cleaned_data:
article = art_item.cleaned_data["article"]
quantity = art_item.cleaned_data["quantity"]
total_price += article.prix * quantity
new_purchase = Vente(
facture=new_invoice_instance,
2017-10-13 20:47:32 +00:00
name=article.name,
prix=article.prix,
duration_connection=article.duration_connection,
duration_days_connection=article.duration_days_connection,
duration_membership=article.duration_membership,
duration_days_membership=article.duration_days_membership,
number=quantity,
2021-02-10 10:06:09 +00:00
)
purchases.append(new_purchase)
p = find_payment_method(new_invoice_instance.paiement)
if hasattr(p, "check_price"):
price_ok, msg = p.check_price(total_price, user)
invoice_form.add_error(None, msg)
else:
price_ok = True
if price_ok:
new_invoice_instance.save()
2019-12-15 15:23:53 +00:00
# Saving purchases so the invoice can find them. Invoice
# will modify them after being validated to put the right dates.
for p in purchases:
p.facture = new_invoice_instance
p.save()
return new_invoice_instance.paiement.end_payment(
new_invoice_instance, request
)
else:
messages.error(request, _("You need to choose at least one article."))
2018-07-10 16:06:58 +00:00
p = Paiement.objects.filter(is_balance=True)
if len(p) and p[0].can_use_payment(request.user):
balance = user.solde
else:
balance = None
2018-07-18 00:08:03 +00:00
return form(
{
"factureform": invoice_form,
"articlesformset": article_formset,
"articlelist": article_list,
"balance": balance,
"action_name": _("Confirm"),
"title": _("New invoice"),
},
"cotisations/facture.html",
request,
)
2016-07-02 13:58:50 +00:00
2018-12-31 22:58:37 +00:00
@login_required
@can_create(CostEstimate)
def new_cost_estimate(request):
"""
View used to generate a custom invoice. It's mainly used to
get invoices that are not taken into account, for the administrative
point of view.
"""
# The template needs the list of articles (for the JS part)
2019-12-27 16:00:20 +00:00
articles = Article.objects.all()
2020-05-30 09:00:39 +00:00
# Building the invoice form and the article formset
2018-12-31 22:58:37 +00:00
cost_estimate_form = CostEstimateForm(request.POST or None)
articles_formset = formset_factory(SelectArticleForm)(
request.POST or None, form_kwargs={"user": request.user}
2018-12-31 22:58:37 +00:00
)
discount_form = DiscountForm(request.POST or None)
if (
cost_estimate_form.is_valid()
and articles_formset.is_valid()
and discount_form.is_valid()
):
2018-12-31 22:58:37 +00:00
cost_estimate_instance = cost_estimate_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"]
2018-12-31 22:58:37 +00:00
Vente.objects.create(
facture=cost_estimate_instance,
name=article.name,
prix=article.prix,
type_cotisation=article.type_cotisation,
duration=article.duration,
number=quantity,
2018-12-31 22:58:37 +00:00
)
discount_form.apply_to_invoice(cost_estimate_instance)
2019-01-10 23:39:16 +00:00
messages.success(request, _("The cost estimate was created."))
return redirect(reverse("cotisations:index-cost-estimate"))
2018-12-31 22:58:37 +00:00
return form(
{
"factureform": cost_estimate_form,
"action_name": _("Confirm"),
"articlesformset": articles_formset,
"articlelist": articles,
"discount_form": discount_form,
"title": _("New cost estimate"),
},
"cotisations/facture.html",
request,
)
2018-12-31 22:58:37 +00:00
2016-07-08 10:35:53 +00:00
@login_required
2018-07-21 22:16:05 +00:00
@can_create(CustomInvoice)
def new_custom_invoice(request):
"""
2018-07-21 22:16:05 +00:00
View used to generate a custom invoice. It's mainly used to
get invoices that are not taken into account, for the administrative
point of view.
"""
# The template needs the list of articles (for the JS part)
2019-12-27 16:00:20 +00:00
articles = Article.objects.all()
2020-05-30 09:00:39 +00:00
# Building the invoice form and the article formset
2018-07-21 22:16:05 +00:00
invoice_form = CustomInvoiceForm(request.POST or None)
articles_formset = formset_factory(SelectArticleForm)(
request.POST or None, form_kwargs={"user": request.user}
)
2018-12-29 13:01:22 +00:00
discount_form = DiscountForm(request.POST or None)
if (
invoice_form.is_valid()
and articles_formset.is_valid()
and discount_form.is_valid()
):
2018-07-21 22:16:05 +00:00
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"]
2018-07-21 22:16:05 +00:00
Vente.objects.create(
facture=new_invoice_instance,
name=article.name,
prix=article.prix,
duration_membership=article.duration_membership,
duration_days_membership=article.duration_membership,
duration_connection=article.duration_connection,
duration_days_connection=article.duration_days_connection,
number=quantity,
2018-07-21 22:16:05 +00:00
)
2018-12-29 13:01:22 +00:00
discount_form.apply_to_invoice(new_invoice_instance)
messages.success(request, _("The custom invoice was created."))
return redirect(reverse("cotisations:index-custom-invoice"))
2018-07-21 22:16:05 +00:00
return form(
{
"factureform": invoice_form,
"action_name": _("Confirm"),
"articlesformset": articles_formset,
"articlelist": articles,
"discount_form": discount_form,
"title": _("New custom invoice"),
},
"cotisations/facture.html",
request,
)
# TODO : change facture to invoice
2016-07-11 22:05:07 +00:00
@login_required
2017-12-27 20:09:00 +00:00
@can_view(Facture)
2018-04-15 01:00:05 +00:00
def facture_pdf(request, facture, **_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=facture)
# 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": True,
"fid": facture.id,
"DATE": facture.date,
"recipient_name": "{} {}".format(facture.user.name, facture.user.surname),
"address": facture.user.room,
"article": purchases_info,
"total": facture.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),
"payment_method": facture.paiement.moyen,
},
)
2016-07-11 22:05:07 +00:00
# TODO : change facture to invoice
2016-07-13 23:54:06 +00:00
@login_required
@can_edit(Facture)
2018-04-15 01:00:05 +00:00
def edit_facture(request, facture, **_kwargs):
"""
View used to edit an existing invoice.
2018-07-21 22:16:05 +00:00
Articles can be added or removed to the invoice and quantity
can be set as desired. This is also the view used to invalidate
an invoice.
"""
2018-07-10 16:38:05 +00:00
invoice_form = FactureForm(
request.POST or None, instance=facture, user=request.user
2018-04-13 18:58:29 +00:00
)
purchases_objects = Vente.objects.filter(facture=facture)
purchase_form_set = modelformset_factory(
Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects)
2018-04-13 18:58:29 +00:00
)
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 was edited."))
return redirect(reverse("cotisations:index"))
return form(
{"factureform": invoice_form, "venteform": purchase_form},
"cotisations/edit_facture.html",
request,
)
2016-07-02 13:58:50 +00:00
# TODO : change facture to invoice
@login_required
@can_delete(Facture)
2018-04-15 01:00:05 +00:00
def del_facture(request, facture, **_kwargs):
"""
2020-05-30 09:00:39 +00:00
View used to delete an existing invoice.
"""
if request.method == "POST":
facture.delete()
messages.success(request, _("The invoice was deleted."))
return redirect(reverse("cotisations:index"))
return form(
{"objet": facture, "objet_name": _("invoice")},
"cotisations/delete.html",
request,
)
2018-12-31 22:58:37 +00:00
@login_required
@can_edit(CostEstimate)
def edit_cost_estimate(request, invoice, **kwargs):
2020-05-30 09:00:39 +00:00
# Building the invoice form and the article formset
invoice_form = CostEstimateForm(request.POST or None, instance=invoice)
2018-12-31 22:58:37 +00:00
purchases_objects = Vente.objects.filter(facture=invoice)
purchase_form_set = modelformset_factory(
Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects)
2018-12-31 22:58:37 +00:00
)
purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects)
2018-12-31 22:58:37 +00:00
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 cost estimate was edited."))
return redirect(reverse("cotisations:index-cost-estimate"))
2018-12-31 22:58:37 +00:00
return form(
{
"factureform": invoice_form,
"venteform": purchase_form,
"title": _("Edit cost estimate"),
},
"cotisations/edit_facture.html",
request,
)
2018-12-31 22:58:37 +00:00
@login_required
@can_edit(CostEstimate)
@can_create(CustomInvoice)
def cost_estimate_to_invoice(request, cost_estimate, **_kwargs):
2020-05-30 09:00:39 +00:00
"""Create a custom invoice from a cost estimate."""
2018-12-31 22:58:37 +00:00
cost_estimate.create_invoice()
messages.success(
request, _("An invoice was successfully created from your cost estimate.")
2018-12-31 22:58:37 +00:00
)
return redirect(reverse("cotisations:index-custom-invoice"))
2018-12-31 22:58:37 +00:00
2018-07-21 22:16:05 +00:00
@login_required
@can_edit(CustomInvoice)
def edit_custom_invoice(request, invoice, **kwargs):
2020-05-30 09:00:39 +00:00
# Building the invoice form and the article formset
invoice_form = CustomInvoiceForm(request.POST or None, instance=invoice)
2018-07-21 22:16:05 +00:00
purchases_objects = Vente.objects.filter(facture=invoice)
purchase_form_set = modelformset_factory(
Vente, fields=("name", "number"), extra=0, max_num=len(purchases_objects)
2018-07-21 22:16:05 +00:00
)
purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects)
2018-07-21 22:16:05 +00:00
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 was edited."))
return redirect(reverse("cotisations:index-custom-invoice"))
2018-07-21 22:16:05 +00:00
return form(
{
"factureform": invoice_form,
"venteform": purchase_form,
"title": _("Edit custom invoice"),
},
"cotisations/edit_facture.html",
request,
)
2018-07-21 22:16:05 +00:00
2018-12-31 22:58:37 +00:00
@login_required
@can_view(CostEstimate)
def cost_estimate_pdf(request, invoice, **_kwargs):
"""
View used to generate a PDF file from an existing cost estimate 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.
"""
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": escape_chars(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),
"payment_method": invoice.payment,
"remark": invoice.remark,
"end_validity": invoice.date + invoice.validity,
"is_estimate": True,
},
)
2018-12-31 22:58:37 +00:00
@login_required
@can_delete(CostEstimate)
def del_cost_estimate(request, estimate, **_kwargs):
"""
2020-05-30 09:00:39 +00:00
View used to delete an existing invoice.
2018-12-31 22:58:37 +00:00
"""
if request.method == "POST":
estimate.delete()
messages.success(request, _("The cost estimate was deleted."))
return redirect(reverse("cotisations:index-cost-estimate"))
return form(
{"objet": estimate, "objet_name": _("cost estimate")},
"cotisations/delete.html",
request,
)
2018-12-31 22:58:37 +00:00
2018-07-21 22:16:05 +00:00
@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": escape_chars(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),
"payment_method": invoice.payment,
"remark": invoice.remark,
},
)
2018-07-21 22:16:05 +00:00
@login_required
@can_delete(CustomInvoice)
def del_custom_invoice(request, invoice, **_kwargs):
"""
2020-05-30 09:00:39 +00:00
View used to delete an existing invoice.
2018-07-21 22:16:05 +00:00
"""
if request.method == "POST":
invoice.delete()
messages.success(request, _("The invoice was deleted."))
return redirect(reverse("cotisations:index-custom-invoice"))
return form(
{"objet": invoice, "objet_name": _("invoice")},
"cotisations/delete.html",
request,
)
2018-07-21 22:16:05 +00:00
2016-07-08 10:35:53 +00:00
@login_required
@can_create(Article)
2016-07-06 18:57:31 +00:00
def add_article(request):
"""
View used to add an article.
2018-04-13 18:58:29 +00:00
.. 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.
"""
2016-07-06 18:57:31 +00:00
article = ArticleForm(request.POST or None)
if article.is_valid():
article.save()
messages.success(request, _("The article was created."))
return redirect(reverse("cotisations:index-article"))
return form(
{
"factureform": article,
"action_name": _("Add"),
"title": _("New article"),
},
"cotisations/facture.html",
request,
)
2016-07-08 10:35:53 +00:00
@login_required
@can_edit(Article)
2018-04-15 01:00:05 +00:00
def edit_article(request, article_instance, **_kwargs):
"""
View used to edit an article.
"""
article = ArticleForm(request.POST or None, instance=article_instance)
if article.is_valid():
2018-03-31 22:06:44 +00:00
if article.changed_data:
article.save()
messages.success(request, _("The article was edited."))
return redirect(reverse("cotisations:index-article"))
return form(
{
"factureform": article,
"action_name": _("Edit"),
"title": _("Edit article"),
},
"cotisations/facture.html",
request,
)
2016-07-06 18:57:31 +00:00
2016-07-08 10:35:53 +00:00
@login_required
@can_delete_set(Article)
def del_article(request, instances):
"""
View used to delete one of the articles.
"""
article = DelArticleForm(request.POST or None, instances=instances)
2016-07-06 18:57:31 +00:00
if article.is_valid():
article_del = article.cleaned_data["articles"]
article_del.delete()
messages.success(request, _("The articles were deleted."))
return redirect(reverse("cotisations:index-article"))
return form(
{
"factureform": article,
"action_name": _("Delete"),
"title": _("Delete article"),
},
"cotisations/facture.html",
request,
)
2016-07-06 18:57:31 +00:00
# TODO : change paiement to payment
2016-07-08 10:35:53 +00:00
@login_required
@can_create(Paiement)
def add_paiement(request):
"""
View used to add a payment method.
"""
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()
2018-07-05 13:21:51 +00:00
payment_method.save(payment)
messages.success(request, _("The payment method was created."))
return redirect(reverse("cotisations:index-paiement"))
return form(
{
"factureform": payment,
"payment_method": payment_method,
"action_name": _("Add"),
"title": _("New payment method"),
},
"cotisations/facture.html",
request,
)
# TODO : chnage paiement to Payment
2016-07-08 10:35:53 +00:00
@login_required
@can_edit(Paiement)
2018-04-15 13:34:51 +00:00
def edit_paiement(request, paiement_instance, **_kwargs):
"""
View used to edit a payment method.
"""
payment = PaiementForm(
request.POST or None, instance=paiement_instance, prefix="payment"
)
payment_method = payment_method_factory(
paiement_instance, request.POST or None, prefix="payment_method", creation=False
)
if payment.is_valid() and (payment_method is None or payment_method.is_valid()):
payment.save()
if payment_method is not None:
payment_method.save()
messages.success(request, _("The payment method was edited."))
return redirect(reverse("cotisations:index-paiement"))
return form(
{
"factureform": payment,
"payment_method": payment_method,
"action_name": _("Edit"),
"title": _("Edit payment method"),
},
"cotisations/facture.html",
request,
)
# TODO : change paiement to payment
2016-07-08 10:35:53 +00:00
@login_required
@can_delete_set(Paiement)
def del_paiement(request, instances):
"""
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:
try:
payment_del.delete()
messages.success(
2017-10-13 20:47:32 +00:00
request,
_("The payment method %(method_name)s was deleted.")
% {"method_name": payment_del},
)
except ProtectedError:
messages.error(
request,
_(
"The payment method %(method_name)s can't be deleted"
" because there are invoices using it."
)
% {"method_name": payment_del},
)
return redirect(reverse("cotisations:index-paiement"))
return form(
{
"factureform": payment,
"action_name": _("Delete"),
"title": _("Delete payment method"),
},
"cotisations/facture.html",
request,
)
# TODO : change banque to bank
2016-07-08 10:35:53 +00:00
@login_required
@can_create(Banque)
2016-07-06 20:20:49 +00:00
def add_banque(request):
"""
View used to add a bank.
"""
bank = BanqueForm(request.POST or None)
if bank.is_valid():
bank.save()
messages.success(request, _("The bank was created."))
return redirect(reverse("cotisations:index-banque"))
return form(
{
"factureform": bank,
"action_name": _("Add"),
"title": _("New bank"),
},
"cotisations/facture.html",
request,
)
# TODO : change banque to bank
2016-07-08 10:35:53 +00:00
@login_required
@can_edit(Banque)
2018-04-15 01:00:05 +00:00
def edit_banque(request, banque_instance, **_kwargs):
"""
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()
messages.success(request, _("The bank was edited."))
return redirect(reverse("cotisations:index-banque"))
return form(
{
"factureform": bank,
"action_name": _("Edit"),
"title": _("Edit bank"),
},
"cotisations/facture.html",
request,
)
2016-07-06 20:20:49 +00:00
# TODO : chnage banque to bank
2016-07-08 10:35:53 +00:00
@login_required
@can_delete_set(Banque)
def del_banque(request, instances):
"""
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:
2016-07-06 20:20:49 +00:00
try:
bank_del.delete()
messages.success(
request,
_("The bank %(bank_name)s was deleted.") % {"bank_name": bank_del},
)
2016-07-06 20:20:49 +00:00
except ProtectedError:
messages.error(
request,
_(
"The bank %(bank_name)s can't be deleted because there"
" are invoices using it."
)
% {"bank_name": bank_del},
)
return redirect(reverse("cotisations:index-banque"))
return form(
{
"factureform": bank,
"action_name": _("Delete"),
"title": _("Delete bank"),
},
"cotisations/facture.html",
request,
)
2016-07-06 20:20:49 +00:00
# TODO : change facture to invoice
@login_required
2017-12-27 20:09:00 +00:00
@can_view_all(Facture)
@can_change(Facture, "control")
def control(request):
"""
View used to control the invoices all at once.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
invoice_list = Facture.objects.select_related("user").select_related("paiement")
invoice_list = SortTable.sort(
invoice_list,
request.GET.get("col"),
request.GET.get("order"),
SortTable.COTISATIONS_CONTROL,
)
control_invoices_formset = modelformset_factory(
Facture, fields=("control", "valid"), extra=0
)
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()
reversion.set_comment("Control")
messages.success(
request, _("Your changes have been properly taken into account.")
)
return redirect(reverse("cotisations:control"))
return render(
request,
"cotisations/control.html",
{"facture_list": invoice_list, "controlform": control_invoices_form},
)
2016-07-08 10:35:53 +00:00
@login_required
2017-12-27 20:09:00 +00:00
@can_view_all(Article)
def index_article(request):
"""
View used to display the list of all available articles.
"""
# TODO : Offer other means of sorting
article_list = Article.objects.order_by("name")
return render(
request, "cotisations/index_article.html", {"article_list": article_list}
)
# TODO : change paiement to payment
2016-07-08 10:35:53 +00:00
@login_required
2017-12-27 20:09:00 +00:00
@can_view_all(Paiement)
def index_paiement(request):
"""
View used to display the list of all available payment methods.
"""
payment_list = Paiement.objects.order_by("moyen")
return render(
request, "cotisations/index_paiement.html", {"paiement_list": payment_list}
)
# TODO : change banque to bank
2016-07-08 10:35:53 +00:00
@login_required
2017-12-27 20:09:00 +00:00
@can_view_all(Banque)
def index_banque(request):
"""
View used to display the list of all available banks.
"""
bank_list = Banque.objects.order_by("name")
return render(request, "cotisations/index_banque.html", {"banque_list": bank_list})
2018-12-31 22:58:37 +00:00
@login_required
@can_view_all(CustomInvoice)
def index_cost_estimate(request):
"""View used to display every custom invoice."""
pagination_number = GeneralOption.get_cached_value("pagination_number")
cost_estimate_list = CostEstimate.objects.prefetch_related("vente_set")
2018-12-31 22:58:37 +00:00
cost_estimate_list = SortTable.sort(
cost_estimate_list,
request.GET.get("col"),
request.GET.get("order"),
SortTable.COTISATIONS_CUSTOM,
2018-12-31 22:58:37 +00:00
)
cost_estimate_list = re2o_paginator(request, cost_estimate_list, pagination_number)
return render(
2018-12-31 22:58:37 +00:00
request,
"cotisations/index_cost_estimate.html",
{"cost_estimate_list": cost_estimate_list},
2018-12-31 22:58:37 +00:00
)
2018-07-21 22:16:05 +00:00
@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")
cost_estimate_ids = [i for i, in CostEstimate.objects.values_list("id")]
custom_invoice_list = CustomInvoice.objects.prefetch_related("vente_set").exclude(
id__in=cost_estimate_ids
)
2018-07-21 22:16:05 +00:00
custom_invoice_list = SortTable.sort(
custom_invoice_list,
request.GET.get("col"),
request.GET.get("order"),
SortTable.COTISATIONS_CUSTOM,
2018-07-21 22:16:05 +00:00
)
custom_invoice_list = re2o_paginator(
request, custom_invoice_list, pagination_number
)
return render(
2018-07-21 22:16:05 +00:00
request,
"cotisations/index_custom_invoice.html",
{"custom_invoice_list": custom_invoice_list},
2018-07-21 22:16:05 +00:00
)
2016-07-08 10:35:53 +00:00
@login_required
2017-12-27 20:09:00 +00:00
@can_view_all(Facture)
2018-07-21 22:16:05 +00:00
@can_view_all(CustomInvoice)
2016-07-02 13:58:50 +00:00
def index(request):
"""
View used to display the list of all exisitng invoices.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
invoice_list = (
Facture.objects.select_related("user")
.select_related("paiement")
.prefetch_related("vente_set")
)
invoice_list = SortTable.sort(
invoice_list,
request.GET.get("col"),
request.GET.get("order"),
SortTable.COTISATIONS_INDEX,
)
invoice_list = re2o_paginator(request, invoice_list, pagination_number)
return render(request, "cotisations/index.html", {"facture_list": invoice_list})
# TODO : change solde to balance
2018-01-12 00:07:25 +00:00
@login_required
@can_edit(User)
def credit_solde(request, user, **_kwargs):
"""
View used to edit the balance of a user.
Can be use either to increase or decrease a user's balance.
"""
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}))
2019-01-03 18:52:06 +00:00
refill_form = RechargeForm(
request.POST or None, user=user, user_source=request.user
)
if refill_form.is_valid():
price = refill_form.cleaned_data["value"]
2018-07-10 14:46:49 +00:00
invoice = Facture(user=user)
invoice.paiement = refill_form.cleaned_data["payment"]
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:
2020-04-19 19:19:06 +00:00
invoice.save(request=request)
Vente.objects.create(
facture=invoice,
name="solde",
prix=refill_form.cleaned_data["value"],
number=1,
)
2018-07-22 22:13:25 +00:00
return invoice.paiement.end_payment(invoice, request)
p = get_object_or_404(Paiement, is_balance=True)
return form(
{
"factureform": refill_form,
"balance": user.solde,
"title": _("Refill your balance"),
"action_name": _("Pay"),
"max_balance": find_payment_method(p).maximum_balance,
},
"cotisations/facture.html",
request,
)
2018-06-23 17:54:20 +00:00
2019-01-03 18:52:06 +00:00
2019-01-10 23:39:16 +00:00
@login_required
@can_view(Facture)
def voucher_pdf(request, invoice, **_kwargs):
"""
View used to generate a PDF file from a controlled invoice
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.
"""
if not invoice.control:
messages.error(request, _("Could not find a voucher for that invoice."))
return redirect(reverse("cotisations:index"))
president = Mandate.get_mandate(invoice.date).president
return render_voucher(
request,
{
"asso_name": AssoOption.get_cached_value("name"),
"pres_name": " ".join([president.name, president.surname]),
"firstname": invoice.user.name,
"lastname": invoice.user.surname,
"email": invoice.user.email,
"phone": invoice.user.telephone,
2021-02-10 10:06:09 +00:00
"date_end": invoice.get_subscription()
.latest("date_end_memb")
.date_end_memb,
"date_begin": invoice.date,
},
)
2021-02-10 10:06:09 +00:00
def aff_profil(request, user):
"""View used to display the cotisations on a user's profil."""
factures = Facture.objects.filter(user=user)
factures = SortTable.sort(
factures,
request.GET.get("col"),
request.GET.get("order"),
SortTable.COTISATIONS_INDEX,
2021-02-10 10:06:09 +00:00
)
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
2021-02-10 10:06:09 +00:00
factures = re2o_paginator(request, factures, pagination_large_number)
context = {
2021-02-10 10:06:09 +00:00
"users": user,
"facture_list": factures,
2021-02-10 10:06:09 +00:00
}
return render_to_string(
2021-02-10 10:06:09 +00:00
"cotisations/aff_profil.html", context=context, request=request, using=None
)