1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2024-11-27 02:43:01 +00:00

Billing -> Invoice

This commit is contained in:
Yohann D'ANELLO 2020-03-22 01:22:27 +01:00
parent 18f6daf2ac
commit 4f343fc99f
12 changed files with 333 additions and 251 deletions

View File

@ -3,11 +3,11 @@
from django.contrib import admin from django.contrib import admin
from .models import Billing, Product from .models import Invoice, Product
@admin.register(Billing) @admin.register(Invoice)
class BillingAdmin(admin.ModelAdmin): class InvoiceAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'subject', 'acquitted', ) list_display = ('id', 'name', 'subject', 'acquitted', )

View File

@ -4,17 +4,17 @@
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from django import forms from django import forms
from .models import Billing, Product from .models import Invoice, Product
class BillingForm(forms.ModelForm): class InvoiceForm(forms.ModelForm):
class Meta: class Meta:
model = Billing model = Invoice
fields = '__all__' fields = '__all__'
ProductFormSet = forms.inlineformset_factory( ProductFormSet = forms.inlineformset_factory(
Billing, Invoice,
Product, Product,
fields='__all__', fields='__all__',
extra=1, extra=1,

View File

@ -5,10 +5,10 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
class Billing(models.Model): class Invoice(models.Model):
id = models.PositiveIntegerField( id = models.PositiveIntegerField(
primary_key=True, primary_key=True,
verbose_name=_("Billing identifier"), verbose_name=_("Invoice identifier"),
) )
bde = models.CharField( bde = models.CharField(
@ -104,8 +104,8 @@ class Billing(models.Model):
class Product(models.Model): class Product(models.Model):
billing = models.ForeignKey( invoice = models.ForeignKey(
Billing, Invoice,
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )

View File

@ -5,16 +5,16 @@ import django_tables2 as tables
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_tables2 import A from django_tables2 import A
from .models import Billing from .models import Invoice
class BillingTable(tables.Table): class InvoiceTable(tables.Table):
id = tables.LinkColumn("treasury:billing_update", id = tables.LinkColumn("treasury:invoice_update",
args=[A("pk")], args=[A("pk")],
text=lambda record: _("Billing #{:d}").format(record.id), ) text=lambda record: _("Invoice #{:d}").format(record.id), )
billing = tables.LinkColumn("treasury:billing_render", invoice = tables.LinkColumn("treasury:invoice_render",
verbose_name=_("Billing"), verbose_name=_("Invoice"),
args=[A("pk")], args=[A("pk")],
accessor="pk", accessor="pk",
text="", text="",
@ -27,6 +27,6 @@ class BillingTable(tables.Table):
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
} }
model = Billing model = Invoice
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('id', 'name', 'subject', 'acquitted', 'billing',) fields = ('id', 'name', 'subject', 'acquitted', 'invoice',)

View File

@ -3,12 +3,12 @@
from django.urls import path from django.urls import path
from .views import BillingCreateView, BillingListView, BillingUpdateView, BillingRenderView from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceRenderView
app_name = 'treasury' app_name = 'treasury'
urlpatterns = [ urlpatterns = [
path('billing/', BillingListView.as_view(), name='billing'), path('invoice/', InvoiceListView.as_view(), name='invoice'),
path('billing/create/', BillingCreateView.as_view(), name='billing_create'), path('invoice/create/', InvoiceCreateView.as_view(), name='invoice_create'),
path('billing/<int:pk>/', BillingUpdateView.as_view(), name='billing_update'), path('invoice/<int:pk>/', InvoiceUpdateView.as_view(), name='invoice_update'),
path('billing/render/<int:pk>/', BillingRenderView.as_view(), name='billing_render'), path('invoice/render/<int:pk>/', InvoiceRenderView.as_view(), name='invoice_render'),
] ]

View File

@ -17,17 +17,17 @@ from django.views.generic.base import View
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from note_kfet.settings.base import BASE_DIR from note_kfet.settings.base import BASE_DIR
from .forms import BillingForm, ProductFormSet, ProductFormSetHelper from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper
from .models import Billing, Product from .models import Invoice, Product
from .tables import BillingTable from .tables import InvoiceTable
class BillingCreateView(LoginRequiredMixin, CreateView): class InvoiceCreateView(LoginRequiredMixin, CreateView):
""" """
Create Billing Create Invoice
""" """
model = Billing model = Invoice
form_class = BillingForm form_class = InvoiceForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -64,23 +64,23 @@ class BillingCreateView(LoginRequiredMixin, CreateView):
return ret return ret
def get_success_url(self): def get_success_url(self):
return reverse_lazy('treasury:billing') return reverse_lazy('treasury:invoice')
class BillingListView(LoginRequiredMixin, SingleTableView): class InvoiceListView(LoginRequiredMixin, SingleTableView):
""" """
List existing Billings List existing Invoices
""" """
model = Billing model = Invoice
table_class = BillingTable table_class = InvoiceTable
class BillingUpdateView(LoginRequiredMixin, UpdateView): class InvoiceUpdateView(LoginRequiredMixin, UpdateView):
""" """
Create Billing Create Invoice
""" """
model = Billing model = Invoice
form_class = BillingForm form_class = InvoiceForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -119,27 +119,27 @@ class BillingUpdateView(LoginRequiredMixin, UpdateView):
else: else:
f.instance = None f.instance = None
Product.objects.filter(~Q(pk__in=saved), billing=form.instance).delete() Product.objects.filter(~Q(pk__in=saved), invoice=form.instance).delete()
return ret return ret
def get_success_url(self): def get_success_url(self):
return reverse_lazy('treasury:billing') return reverse_lazy('treasury:invoice')
class BillingRenderView(LoginRequiredMixin, View): class InvoiceRenderView(LoginRequiredMixin, View):
""" """
Render Billing as generated PDF Render Invoice as generated PDF
""" """
def get(self, request, **kwargs): def get(self, request, **kwargs):
pk = kwargs["pk"] pk = kwargs["pk"]
billing = Billing.objects.get(pk=pk) invoice = Invoice.objects.get(pk=pk)
products = Product.objects.filter(billing=billing).all() products = Product.objects.filter(invoice=invoice).all()
billing.description = billing.description.replace("\n", "\\newline\n") invoice.description = invoice.description.replace("\n", "\\newline\n")
billing.address = billing.address.replace("\n", "\\newline\n") invoice.address = invoice.address.replace("\n", "\\newline\n")
tex = render_to_string("treasury/billing_sample.tex", dict(obj=billing, products=products)) tex = render_to_string("treasury/invoice_sample.tex", dict(obj=invoice, products=products))
try: try:
os.mkdir(BASE_DIR + "/tmp") os.mkdir(BASE_DIR + "/tmp")
except FileExistsError: except FileExistsError:
@ -147,13 +147,13 @@ class BillingRenderView(LoginRequiredMixin, View):
tmp_dir = mkdtemp(prefix=BASE_DIR + "/tmp/") tmp_dir = mkdtemp(prefix=BASE_DIR + "/tmp/")
try: try:
with open("{}/billing-{:d}.tex".format(tmp_dir, pk), "wb") as f: with open("{}/invoice-{:d}.tex".format(tmp_dir, pk), "wb") as f:
f.write(tex.encode("UTF-8")) f.write(tex.encode("UTF-8"))
del tex del tex
for _ in range(2): for _ in range(2):
error = subprocess.Popen( error = subprocess.Popen(
["pdflatex", "billing-{}.tex".format(pk)], ["pdflatex", "invoice-{}.tex".format(pk)],
cwd=tmp_dir, cwd=tmp_dir,
stdin=open(os.devnull, "r"), stdin=open(os.devnull, "r"),
stderr=open(os.devnull, "wb"), stderr=open(os.devnull, "wb"),
@ -161,11 +161,11 @@ class BillingRenderView(LoginRequiredMixin, View):
).wait() ).wait()
if error: if error:
raise IOError("An error attempted while generating a billing (code=" + str(error) + ")") raise IOError("An error attempted while generating a invoice (code=" + str(error) + ")")
pdf = open("{}/billing-{}.pdf".format(tmp_dir, pk), 'rb').read() pdf = open("{}/invoice-{}.pdf".format(tmp_dir, pk), 'rb').read()
response = HttpResponse(pdf, content_type="application/pdf") response = HttpResponse(pdf, content_type="application/pdf")
response['Content-Disposition'] = "inline;filename=billing-{:d}.pdf".format(pk) response['Content-Disposition'] = "inline;filename=invoice-{:d}.pdf".format(pk)
except IOError as e: except IOError as e:
raise e raise e
finally: finally:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-21 17:00+0100\n" "POT-Creation-Date: 2020-03-22 01:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -23,9 +23,9 @@ msgid "activity"
msgstr "" msgstr ""
#: apps/activity/models.py:19 apps/activity/models.py:44 #: apps/activity/models.py:19 apps/activity/models.py:44
#: apps/member/models.py:61 apps/member/models.py:112 #: apps/member/models.py:63 apps/member/models.py:114
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198
#: templates/member/profile_detail.html:15 #: templates/member/profile_detail.html:15
msgid "name" msgid "name"
msgstr "" msgstr ""
@ -52,7 +52,7 @@ msgid "description"
msgstr "" msgstr ""
#: apps/activity/models.py:54 apps/note/models/notes.py:164 #: apps/activity/models.py:54 apps/note/models/notes.py:164
#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115 #: apps/note/models/transactions.py:62
msgid "type" msgid "type"
msgstr "" msgstr ""
@ -144,114 +144,114 @@ msgstr ""
msgid "member" msgid "member"
msgstr "" msgstr ""
#: apps/member/models.py:23 #: apps/member/models.py:25
msgid "phone number" msgid "phone number"
msgstr "" msgstr ""
#: apps/member/models.py:29 templates/member/profile_detail.html:28 #: apps/member/models.py:31 templates/member/profile_detail.html:28
msgid "section" msgid "section"
msgstr "" msgstr ""
#: apps/member/models.py:30 #: apps/member/models.py:32
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
msgstr "" msgstr ""
#: apps/member/models.py:36 templates/member/profile_detail.html:31 #: apps/member/models.py:38 templates/member/profile_detail.html:31
msgid "address" msgid "address"
msgstr "" msgstr ""
#: apps/member/models.py:42 #: apps/member/models.py:44
msgid "paid" msgid "paid"
msgstr "" msgstr ""
#: apps/member/models.py:47 apps/member/models.py:48 #: apps/member/models.py:49 apps/member/models.py:50
msgid "user profile" msgid "user profile"
msgstr "" msgstr ""
#: apps/member/models.py:66 #: apps/member/models.py:68
msgid "email" msgid "email"
msgstr "" msgstr ""
#: apps/member/models.py:71 #: apps/member/models.py:73
msgid "membership fee" msgid "membership fee"
msgstr "" msgstr ""
#: apps/member/models.py:75 #: apps/member/models.py:77
msgid "membership duration" msgid "membership duration"
msgstr "" msgstr ""
#: apps/member/models.py:76 #: apps/member/models.py:78
msgid "The longest time a membership can last (NULL = infinite)." msgid "The longest time a membership can last (NULL = infinite)."
msgstr "" msgstr ""
#: apps/member/models.py:81 #: apps/member/models.py:83
msgid "membership start" msgid "membership start"
msgstr "" msgstr ""
#: apps/member/models.py:82 #: apps/member/models.py:84
msgid "How long after January 1st the members can renew their membership." msgid "How long after January 1st the members can renew their membership."
msgstr "" msgstr ""
#: apps/member/models.py:87 #: apps/member/models.py:89
msgid "membership end" msgid "membership end"
msgstr "" msgstr ""
#: apps/member/models.py:88 #: apps/member/models.py:90
msgid "" msgid ""
"How long the membership can last after January 1st of the next year after " "How long the membership can last after January 1st of the next year after "
"members can renew their membership." "members can renew their membership."
msgstr "" msgstr ""
#: apps/member/models.py:94 apps/note/models/notes.py:139 #: apps/member/models.py:96 apps/note/models/notes.py:139
msgid "club" msgid "club"
msgstr "" msgstr ""
#: apps/member/models.py:95 #: apps/member/models.py:97
msgid "clubs" msgid "clubs"
msgstr "" msgstr ""
#: apps/member/models.py:118 apps/permission/models.py:276 #: apps/member/models.py:120 apps/permission/models.py:276
msgid "role" msgid "role"
msgstr "" msgstr ""
#: apps/member/models.py:119 #: apps/member/models.py:121
msgid "roles" msgid "roles"
msgstr "" msgstr ""
#: apps/member/models.py:143 #: apps/member/models.py:145
msgid "membership starts on" msgid "membership starts on"
msgstr "" msgstr ""
#: apps/member/models.py:146 #: apps/member/models.py:148
msgid "membership ends on" msgid "membership ends on"
msgstr "" msgstr ""
#: apps/member/models.py:150 #: apps/member/models.py:152
msgid "fee" msgid "fee"
msgstr "" msgstr ""
#: apps/member/models.py:154 #: apps/member/models.py:162
msgid "membership" msgid "membership"
msgstr "" msgstr ""
#: apps/member/models.py:155 #: apps/member/models.py:163
msgid "memberships" msgid "memberships"
msgstr "" msgstr ""
#: apps/member/views.py:69 templates/member/profile_detail.html:46 #: apps/member/views.py:80 templates/member/profile_detail.html:46
msgid "Update Profile" msgid "Update Profile"
msgstr "" msgstr ""
#: apps/member/views.py:82 #: apps/member/views.py:93
msgid "An alias with a similar name already exists." msgid "An alias with a similar name already exists."
msgstr "" msgstr ""
#: apps/member/views.py:132 #: apps/member/views.py:146
#, python-format #, python-format
msgid "Account #%(id)s: %(username)s" msgid "Account #%(id)s: %(username)s"
msgstr "" msgstr ""
#: apps/member/views.py:202 #: apps/member/views.py:216
msgid "Alias successfully deleted" msgid "Alias successfully deleted"
msgstr "" msgstr ""
@ -416,70 +416,61 @@ msgstr ""
msgid "quantity" msgid "quantity"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15 #: apps/note/models/transactions.py:115
msgid "Gift"
msgstr ""
#: apps/note/models/transactions.py:118 templates/base.html:90
#: templates/note/transaction_form.html:19
#: templates/note/transaction_form.html:126
msgid "Transfer"
msgstr ""
#: apps/note/models/transactions.py:119
msgid "Template"
msgstr ""
#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
msgid "Credit"
msgstr ""
#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
msgid "Debit"
msgstr ""
#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
msgid "membership transaction"
msgstr ""
#: apps/note/models/transactions.py:129
msgid "reason" msgid "reason"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:133 #: apps/note/models/transactions.py:119
msgid "valid" msgid "valid"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:138 #: apps/note/models/transactions.py:124
msgid "transaction" msgid "transaction"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:139 #: apps/note/models/transactions.py:125
msgid "transactions" msgid "transactions"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:190 #: apps/note/models/transactions.py:168 templates/base.html:98
msgid "membership transaction" #: templates/note/transaction_form.html:19
#: templates/note/transaction_form.html:145
msgid "Transfer"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:207 #: apps/note/models/transactions.py:188
msgid "Template"
msgstr ""
#: apps/note/models/transactions.py:203
msgid "first_name" msgid "first_name"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:191 #: apps/note/models/transactions.py:208
#: apps/note/models/transactions.py:212
msgid "bank" msgid "bank"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:24
msgid "Credit"
msgstr ""
#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:28
msgid "Debit"
msgstr ""
#: apps/note/models/transactions.py:230 apps/note/models/transactions.py:235
msgid "membership transaction"
msgstr ""
#: apps/note/models/transactions.py:231 #: apps/note/models/transactions.py:231
msgid "membership transactions" msgid "membership transactions"
msgstr "" msgstr ""
#: apps/note/views.py:31 #: apps/note/views.py:39
msgid "Transfer money" msgid "Transfer money"
msgstr "" msgstr ""
#: apps/note/views.py:144 templates/base.html:70 #: apps/note/views.py:145 templates/base.html:79
msgid "Consumptions" msgid "Consumptions"
msgstr "" msgstr ""
@ -501,12 +492,12 @@ msgstr ""
msgid "Specifying field applies only to view and change permission types." msgid "Specifying field applies only to view and change permission types."
msgstr "" msgstr ""
#: apps/treasury/apps.py:11 templates/base.html:85 #: apps/treasury/apps.py:10 templates/base.html:102
msgid "Treasury" msgid "Treasury"
msgstr "" msgstr ""
#: apps/treasury/models.py:11 #: apps/treasury/models.py:11
msgid "Billing identifier" msgid "Invoice identifier"
msgstr "" msgstr ""
#: apps/treasury/models.py:25 #: apps/treasury/models.py:25
@ -521,7 +512,7 @@ msgstr ""
msgid "Description" msgid "Description"
msgstr "" msgstr ""
#: apps/treasury/models.py:39 #: apps/treasury/models.py:39 templates/note/transaction_form.html:86
msgid "Name" msgid "Name"
msgstr "" msgstr ""
@ -581,30 +572,30 @@ msgstr ""
msgid "Unit price" msgid "Unit price"
msgstr "" msgstr ""
#: apps/note/views.py:132 templates/base.html:78 #: apps/treasury/tables.py:14
msgid "Consumptions" msgid "Invoice #{:d}"
msgstr "" msgstr ""
#: apps/treasury/tables.py:17 #: apps/treasury/tables.py:17
msgid "Billing" msgid "Invoice"
msgstr "" msgstr ""
#: note_kfet/settings/__init__.py:61 #: note_kfet/settings/__init__.py:63
msgid "" msgid ""
"The Central Authentication Service grants you access to most of our websites " "The Central Authentication Service grants you access to most of our websites "
"by authenticating only once, so you don't need to type your credentials " "by authenticating only once, so you don't need to type your credentials "
"again unless your session expires or you logout." "again unless your session expires or you logout."
msgstr "" msgstr ""
#: note_kfet/settings/base.py:157 #: note_kfet/settings/base.py:153
msgid "German" msgid "German"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:158 #: note_kfet/settings/base.py:154
msgid "English" msgid "English"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:159 #: note_kfet/settings/base.py:155
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -612,22 +603,18 @@ msgstr ""
msgid "The ENS Paris-Saclay BDE note." msgid "The ENS Paris-Saclay BDE note."
msgstr "" msgstr ""
#: templates/base.html:81 #: templates/base.html:84
msgid "Clubs" msgid "Clubs"
msgstr "" msgstr ""
#: templates/base.html:84 #: templates/base.html:89
msgid "Activities" msgid "Activities"
msgstr "" msgstr ""
#: templates/base.html:79 #: templates/base.html:94
msgid "Buttons" msgid "Buttons"
msgstr "" msgstr ""
#: templates/base.html:82 templates/note/transaction_form.html:35
msgid "Transfer"
msgstr ""
#: templates/cas_server/base.html:7 #: templates/cas_server/base.html:7
msgid "Central Authentication Service" msgid "Central Authentication Service"
msgstr "" msgstr ""
@ -684,8 +671,7 @@ msgid "Field filters"
msgstr "" msgstr ""
#: templates/django_filters/rest_framework/form.html:5 #: templates/django_filters/rest_framework/form.html:5
#: templates/member/club_form.html:10 templates/treasury/billing_form.html:38 #: templates/member/club_form.html:10 templates/treasury/invoice_form.html:47
#: templates/member/club_form.html:10
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
@ -770,7 +756,7 @@ msgstr ""
msgid "Sign up" msgid "Sign up"
msgstr "" msgstr ""
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38 #: templates/note/conso_form.html:28 templates/note/transaction_form.html:40
msgid "Select emitters" msgid "Select emitters"
msgstr "" msgstr ""
@ -798,55 +784,52 @@ msgstr ""
msgid "Double consumptions" msgid "Double consumptions"
msgstr "" msgstr ""
#: templates/note/conso_form.html:141 #: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
msgid "Recent transactions history" msgid "Recent transactions history"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:55 #: templates/note/transaction_form.html:15
msgid "Gift"
msgstr ""
#: templates/note/transaction_form.html:68
msgid "External payment" msgid "External payment"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:63 #: templates/note/transaction_form.html:76
msgid "Transfer type" msgid "Transfer type"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:73 #: templates/note/transaction_form.html:92
msgid "Name"
msgstr ""
#: templates/note/transaction_form.html:79
msgid "First name" msgid "First name"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:85 #: templates/note/transaction_form.html:98
msgid "Bank" msgid "Bank"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:97 #: templates/note/transaction_form.html:111
#: templates/note/transaction_form.html:179 #: templates/note/transaction_form.html:169
#: templates/note/transaction_form.html:186 #: templates/note/transaction_form.html:176
msgid "Select receivers" msgid "Select receivers"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:114 #: templates/note/transaction_form.html:128
msgid "Amount" msgid "Amount"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:119 #: templates/note/transaction_form.html:138
msgid "Reason" msgid "Reason"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:193 #: templates/note/transaction_form.html:183
msgid "Credit note" msgid "Credit note"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:200 #: templates/note/transaction_form.html:190
msgid "Debit note" msgid "Debit note"
msgstr "" msgstr ""
msgid "Sign up"
msgstr ""
#: templates/note/transactiontemplate_form.html:6 #: templates/note/transactiontemplate_form.html:6
msgid "Buttons list" msgid "Buttons list"
msgstr "" msgstr ""
@ -929,18 +912,18 @@ msgstr ""
msgid "Reset my password" msgid "Reset my password"
msgstr "" msgstr ""
#: templates/treasury/billing_form.html:6 #: templates/treasury/invoice_form.html:6
msgid "Billings list" msgid "Invoices list"
msgstr "" msgstr ""
#: templates/treasury/billing_form.html:33 #: templates/treasury/invoice_form.html:42
msgid "Add product" msgid "Add product"
msgstr "" msgstr ""
#: templates/treasury/billing_form.html:34 #: templates/treasury/invoice_form.html:43
msgid "Remove product" msgid "Remove product"
msgstr "" msgstr ""
#: templates/treasury/billing_list.html:8 #: templates/treasury/invoice_list.html:8
msgid "New billing" msgid "New invoice"
msgstr "" msgstr ""

View File

@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-21 17:00+0100\n" "POT-Creation-Date: 2020-03-22 01:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,9 +18,10 @@ msgid "activity"
msgstr "activité" msgstr "activité"
#: apps/activity/models.py:19 apps/activity/models.py:44 #: apps/activity/models.py:19 apps/activity/models.py:44
#: apps/member/models.py:61 apps/member/models.py:112 #: apps/member/models.py:63 apps/member/models.py:114
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198
#: templates/member/profile_detail.html:15
msgid "name" msgid "name"
msgstr "nom" msgstr "nom"
@ -138,61 +139,61 @@ msgstr "Les logs ne peuvent pas être détruits."
msgid "member" msgid "member"
msgstr "adhérent" msgstr "adhérent"
#: apps/member/models.py:23 #: apps/member/models.py:25
msgid "phone number" msgid "phone number"
msgstr "numéro de téléphone" msgstr "numéro de téléphone"
#: apps/member/models.py:29 templates/member/profile_detail.html:28 #: apps/member/models.py:31 templates/member/profile_detail.html:28
msgid "section" msgid "section"
msgstr "section" msgstr "section"
#: apps/member/models.py:30 #: apps/member/models.py:32
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
#: apps/member/models.py:36 templates/member/profile_detail.html:31 #: apps/member/models.py:38 templates/member/profile_detail.html:31
msgid "address" msgid "address"
msgstr "adresse" msgstr "adresse"
#: apps/member/models.py:42 #: apps/member/models.py:44
msgid "paid" msgid "paid"
msgstr "payé" msgstr "payé"
#: apps/member/models.py:47 apps/member/models.py:48 #: apps/member/models.py:49 apps/member/models.py:50
msgid "user profile" msgid "user profile"
msgstr "profil utilisateur" msgstr "profil utilisateur"
#: apps/member/models.py:66 #: apps/member/models.py:68
msgid "email" msgid "email"
msgstr "courriel" msgstr "courriel"
#: apps/member/models.py:71 #: apps/member/models.py:73
msgid "membership fee" msgid "membership fee"
msgstr "cotisation pour adhérer" msgstr "cotisation pour adhérer"
#: apps/member/models.py:75 #: apps/member/models.py:77
msgid "membership duration" msgid "membership duration"
msgstr "durée de l'adhésion" msgstr "durée de l'adhésion"
#: apps/member/models.py:76 #: apps/member/models.py:78
msgid "The longest time a membership can last (NULL = infinite)." msgid "The longest time a membership can last (NULL = infinite)."
msgstr "La durée maximale d'une adhésion (NULL = infinie)." msgstr "La durée maximale d'une adhésion (NULL = infinie)."
#: apps/member/models.py:81 #: apps/member/models.py:83
msgid "membership start" msgid "membership start"
msgstr "début de l'adhésion" msgstr "début de l'adhésion"
#: apps/member/models.py:82 #: apps/member/models.py:84
msgid "How long after January 1st the members can renew their membership." msgid "How long after January 1st the members can renew their membership."
msgstr "" msgstr ""
"Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur " "Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur "
"adhésion." "adhésion."
#: apps/member/models.py:87 #: apps/member/models.py:89
msgid "membership end" msgid "membership end"
msgstr "fin de l'adhésion" msgstr "fin de l'adhésion"
#: apps/member/models.py:88 #: apps/member/models.py:90
msgid "" msgid ""
"How long the membership can last after January 1st of the next year after " "How long the membership can last after January 1st of the next year after "
"members can renew their membership." "members can renew their membership."
@ -200,65 +201,65 @@ msgstr ""
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année " "Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
"suivante avant que les adhérents peuvent renouveler leur adhésion." "suivante avant que les adhérents peuvent renouveler leur adhésion."
#: apps/member/models.py:94 apps/note/models/notes.py:139 #: apps/member/models.py:96 apps/note/models/notes.py:139
msgid "club" msgid "club"
msgstr "club" msgstr "club"
#: apps/member/models.py:95 #: apps/member/models.py:97
msgid "clubs" msgid "clubs"
msgstr "clubs" msgstr "clubs"
#: apps/member/models.py:118 apps/permission/models.py:276 #: apps/member/models.py:120 apps/permission/models.py:276
msgid "role" msgid "role"
msgstr "rôle" msgstr "rôle"
#: apps/member/models.py:119 #: apps/member/models.py:121
msgid "roles" msgid "roles"
msgstr "rôles" msgstr "rôles"
#: apps/member/models.py:143 #: apps/member/models.py:145
msgid "membership starts on" msgid "membership starts on"
msgstr "l'adhésion commence le" msgstr "l'adhésion commence le"
#: apps/member/models.py:146 #: apps/member/models.py:148
msgid "membership ends on" msgid "membership ends on"
msgstr "l'adhésion finie le" msgstr "l'adhésion finie le"
#: apps/member/models.py:150 #: apps/member/models.py:152
msgid "fee" msgid "fee"
msgstr "cotisation" msgstr "cotisation"
#: apps/member/models.py:154 #: apps/member/models.py:162
msgid "membership" msgid "membership"
msgstr "adhésion" msgstr "adhésion"
#: apps/member/models.py:155 #: apps/member/models.py:163
msgid "memberships" msgid "memberships"
msgstr "adhésions" msgstr "adhésions"
#: apps/member/views.py:69 templates/member/profile_detail.html:46 #: apps/member/views.py:80 templates/member/profile_detail.html:46
msgid "Update Profile" msgid "Update Profile"
msgstr "Modifier le profil" msgstr "Modifier le profil"
#: apps/member/views.py:82 #: apps/member/views.py:93
msgid "An alias with a similar name already exists." msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà." msgstr "Un alias avec un nom similaire existe déjà."
#: apps/member/views.py:132 #: apps/member/views.py:146
#, python-format #, python-format
msgid "Account #%(id)s: %(username)s" msgid "Account #%(id)s: %(username)s"
msgstr "Compte n°%(id)s : %(username)s" msgstr "Compte n°%(id)s : %(username)s"
#: apps/member/views.py:202 #: apps/member/views.py:216
msgid "Alias successfully deleted" msgid "Alias successfully deleted"
msgstr "L'alias a bien été supprimé" msgstr "L'alias a bien été supprimé"
#: apps/note/admin.py:120 apps/note/models/transactions.py:93 #: apps/note/admin.py:120 apps/note/models/transactions.py:94
msgid "source" msgid "source"
msgstr "source" msgstr "source"
#: apps/note/admin.py:128 apps/note/admin.py:156 #: apps/note/admin.py:128 apps/note/admin.py:156
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:99 #: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100
msgid "destination" msgid "destination"
msgstr "destination" msgstr "destination"
@ -278,10 +279,6 @@ msgstr "Choisissez une image"
msgid "Maximal size: 2MB" msgid "Maximal size: 2MB"
msgstr "Taille maximale : 2 Mo" msgstr "Taille maximale : 2 Mo"
#: apps/note/forms.py:70
msgid "Source and destination must be different."
msgstr "La source et la destination doivent être différentes."
#: apps/note/models/notes.py:27 #: apps/note/models/notes.py:27
msgid "account balance" msgid "account balance"
msgstr "solde du compte" msgstr "solde du compte"
@ -313,7 +310,7 @@ msgstr ""
msgid "display image" msgid "display image"
msgstr "image affichée" msgstr "image affichée"
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102 #: apps/note/models/notes.py:53 apps/note/models/transactions.py:103
msgid "created at" msgid "created at"
msgstr "créée le" msgstr "créée le"
@ -399,7 +396,7 @@ msgstr "catégories de transaction"
msgid "A template with this name already exist" msgid "A template with this name already exist"
msgstr "Un modèle de transaction avec un nom similaire existe déjà." msgstr "Un modèle de transaction avec un nom similaire existe déjà."
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:109 #: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111
msgid "amount" msgid "amount"
msgstr "montant" msgstr "montant"
@ -407,47 +404,73 @@ msgstr "montant"
msgid "in centimes" msgid "in centimes"
msgstr "en centimes" msgstr "en centimes"
#: apps/note/models/transactions.py:74 #: apps/note/models/transactions.py:75
msgid "transaction template" msgid "transaction template"
msgstr "modèle de transaction" msgstr "modèle de transaction"
#: apps/note/models/transactions.py:75 #: apps/note/models/transactions.py:76
msgid "transaction templates" msgid "transaction templates"
msgstr "modèles de transaction" msgstr "modèles de transaction"
#: apps/note/models/transactions.py:106 #: apps/note/models/transactions.py:107
msgid "quantity" msgid "quantity"
msgstr "quantité" msgstr "quantité"
#: apps/note/models/transactions.py:111 #: apps/note/models/transactions.py:115
msgid "reason" msgid "reason"
msgstr "raison" msgstr "raison"
#: apps/note/models/transactions.py:115 #: apps/note/models/transactions.py:119
msgid "valid" msgid "valid"
msgstr "valide" msgstr "valide"
#: apps/note/models/transactions.py:120 #: apps/note/models/transactions.py:124
msgid "transaction" msgid "transaction"
msgstr "transaction" msgstr "transaction"
#: apps/note/models/transactions.py:121 #: apps/note/models/transactions.py:125
msgid "transactions" msgid "transactions"
msgstr "transactions" msgstr "transactions"
#: apps/note/models/transactions.py:190 #: apps/note/models/transactions.py:168 templates/base.html:98
#: templates/note/transaction_form.html:19
#: templates/note/transaction_form.html:145
msgid "Transfer"
msgstr "Virement"
#: apps/note/models/transactions.py:188
msgid "Template"
msgstr "Bouton"
#: apps/note/models/transactions.py:203
msgid "first_name"
msgstr "prénom"
#: apps/note/models/transactions.py:208
msgid "bank"
msgstr "banque"
#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:24
msgid "Credit"
msgstr "Crédit"
#: apps/note/models/transactions.py:214 templates/note/transaction_form.html:28
msgid "Debit"
msgstr "Débit"
#: apps/note/models/transactions.py:230 apps/note/models/transactions.py:235
msgid "membership transaction" msgid "membership transaction"
msgstr "transaction d'adhésion" msgstr "transaction d'adhésion"
#: apps/note/models/transactions.py:191 #: apps/note/models/transactions.py:231
msgid "membership transactions" msgid "membership transactions"
msgstr "transactions d'adhésion" msgstr "transactions d'adhésion"
#: apps/note/views.py:31 #: apps/note/views.py:39
msgid "Transfer money from your account to one or others" msgid "Transfer money"
msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres" msgstr "Transférer de l'argent"
#: apps/note/views.py:144 templates/base.html:70 #: apps/note/views.py:145 templates/base.html:79
msgid "Consumptions" msgid "Consumptions"
msgstr "Consommations" msgstr "Consommations"
@ -469,12 +492,12 @@ msgstr "Rang"
msgid "Specifying field applies only to view and change permission types." msgid "Specifying field applies only to view and change permission types."
msgstr "" msgstr ""
#: apps/treasury/apps.py:11 templates/base.html:85 #: apps/treasury/apps.py:10 templates/base.html:102
msgid "Treasury" msgid "Treasury"
msgstr "Trésorerie" msgstr "Trésorerie"
#: apps/treasury/models.py:11 #: apps/treasury/models.py:11
msgid "Billing identifier" msgid "Invoice identifier"
msgstr "Numéro de facture" msgstr "Numéro de facture"
#: apps/treasury/models.py:25 #: apps/treasury/models.py:25
@ -489,7 +512,7 @@ msgstr "Objet"
msgid "Description" msgid "Description"
msgstr "Description" msgstr "Description"
#: apps/treasury/models.py:39 #: apps/treasury/models.py:39 templates/note/transaction_form.html:86
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
@ -550,29 +573,29 @@ msgid "Unit price"
msgstr "Prix unitaire" msgstr "Prix unitaire"
#: apps/treasury/tables.py:14 #: apps/treasury/tables.py:14
msgid "Billing #{:d}" msgid "Invoice #{:d}"
msgstr "Facture n°{:d}" msgstr "Facture n°{:d}"
#: apps/treasury/tables.py:17 #: apps/treasury/tables.py:17
msgid "Billing" msgid "Invoice"
msgstr "Facture" msgstr "Facture"
#: note_kfet/settings/__init__.py:61 #: note_kfet/settings/__init__.py:63
msgid "" msgid ""
"The Central Authentication Service grants you access to most of our websites " "The Central Authentication Service grants you access to most of our websites "
"by authenticating only once, so you don't need to type your credentials " "by authenticating only once, so you don't need to type your credentials "
"again unless your session expires or you logout." "again unless your session expires or you logout."
msgstr "" msgstr ""
#: note_kfet/settings/base.py:157 #: note_kfet/settings/base.py:153
msgid "German" msgid "German"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:158 #: note_kfet/settings/base.py:154
msgid "English" msgid "English"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:159 #: note_kfet/settings/base.py:155
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -580,22 +603,18 @@ msgstr ""
msgid "The ENS Paris-Saclay BDE note." msgid "The ENS Paris-Saclay BDE note."
msgstr "La note du BDE de l'ENS Paris-Saclay." msgstr "La note du BDE de l'ENS Paris-Saclay."
#: templates/base.html:73 #: templates/base.html:84
msgid "Clubs" msgid "Clubs"
msgstr "Clubs" msgstr "Clubs"
#: templates/base.html:76 #: templates/base.html:89
msgid "Activities" msgid "Activities"
msgstr "Activités" msgstr "Activités"
#: templates/base.html:79 #: templates/base.html:94
msgid "Buttons" msgid "Buttons"
msgstr "Boutons" msgstr "Boutons"
#: templates/base.html:82 templates/note/transaction_form.html:35
msgid "Transfer"
msgstr "Virement"
#: templates/cas_server/base.html:7 #: templates/cas_server/base.html:7
msgid "Central Authentication Service" msgid "Central Authentication Service"
msgstr "" msgstr ""
@ -654,9 +673,9 @@ msgid "Field filters"
msgstr "" msgstr ""
#: templates/django_filters/rest_framework/form.html:5 #: templates/django_filters/rest_framework/form.html:5
#: templates/member/club_form.html:10 templates/treasury/billing_form.html:38 #: templates/member/club_form.html:10 templates/treasury/invoice_form.html:47
msgid "Submit" msgid "Submit"
msgstr "" msgstr "Envoyer"
#: templates/member/club_detail.html:10 #: templates/member/club_detail.html:10
msgid "Membership starts on" msgid "Membership starts on"
@ -704,15 +723,15 @@ msgstr "Ajouter un alias"
#: templates/member/profile_detail.html:15 #: templates/member/profile_detail.html:15
msgid "first name" msgid "first name"
msgstr "" msgstr "prénom"
#: templates/member/profile_detail.html:18 #: templates/member/profile_detail.html:18
msgid "username" msgid "username"
msgstr "" msgstr "pseudo"
#: templates/member/profile_detail.html:21 #: templates/member/profile_detail.html:21
msgid "password" msgid "password"
msgstr "" msgstr "mot de passe"
#: templates/member/profile_detail.html:24 #: templates/member/profile_detail.html:24
msgid "Change password" msgid "Change password"
@ -739,6 +758,80 @@ msgstr "Sauvegarder les changements"
msgid "Sign up" msgid "Sign up"
msgstr "Inscription" msgstr "Inscription"
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:40
msgid "Select emitters"
msgstr "Sélection des émetteurs"
#: templates/note/conso_form.html:45
msgid "Select consumptions"
msgstr "Sélection des consommations"
#: templates/note/conso_form.html:51
msgid "Consume!"
msgstr "Consommer !"
#: templates/note/conso_form.html:64
msgid "Most used buttons"
msgstr "Boutons les plus utilisés"
#: templates/note/conso_form.html:121
msgid "Edit"
msgstr "Éditer"
#: templates/note/conso_form.html:126
msgid "Single consumptions"
msgstr "Consommations simples"
#: templates/note/conso_form.html:130
msgid "Double consumptions"
msgstr "Consommations doubles"
#: templates/note/conso_form.html:141 templates/note/transaction_form.html:152
msgid "Recent transactions history"
msgstr "Historique des transactions récentes"
#: templates/note/transaction_form.html:15
msgid "Gift"
msgstr "Don"
#: templates/note/transaction_form.html:68
msgid "External payment"
msgstr "Paiement externe"
#: templates/note/transaction_form.html:76
msgid "Transfer type"
msgstr "Type de transfert"
#: templates/note/transaction_form.html:92
msgid "First name"
msgstr "Prénom"
#: templates/note/transaction_form.html:98
msgid "Bank"
msgstr "Banque"
#: templates/note/transaction_form.html:111
#: templates/note/transaction_form.html:169
#: templates/note/transaction_form.html:176
msgid "Select receivers"
msgstr "Sélection des destinataires"
#: templates/note/transaction_form.html:128
msgid "Amount"
msgstr "Montant"
#: templates/note/transaction_form.html:138
msgid "Reason"
msgstr "Raison"
#: templates/note/transaction_form.html:183
msgid "Credit note"
msgstr "Note à recharger"
#: templates/note/transaction_form.html:190
msgid "Debit note"
msgstr "Note à débiter"
#: templates/note/transactiontemplate_form.html:6 #: templates/note/transactiontemplate_form.html:6
msgid "Buttons list" msgid "Buttons list"
msgstr "Liste des boutons" msgstr "Liste des boutons"
@ -821,18 +914,24 @@ msgstr ""
msgid "Reset my password" msgid "Reset my password"
msgstr "" msgstr ""
#: templates/treasury/billing_form.html:6 #: templates/treasury/invoice_form.html:6
msgid "Billings list" msgid "Invoices list"
msgstr "Liste des factures" msgstr "Liste des factures"
#: templates/treasury/billing_form.html:33 #: templates/treasury/invoice_form.html:42
msgid "Add product" msgid "Add product"
msgstr "Ajouter produit" msgstr "Ajouter produit"
#: templates/treasury/billing_form.html:34 #: templates/treasury/invoice_form.html:43
msgid "Remove product" msgid "Remove product"
msgstr "Retirer produit" msgstr "Retirer produit"
#: templates/treasury/billing_list.html:8 #: templates/treasury/invoice_list.html:8
msgid "New billing" msgid "New invoice"
msgstr "Nouvelle facture" msgstr "Nouvelle facture"
#~ msgid "Source and destination must be different."
#~ msgstr "La source et la destination doivent être différentes."
#~ msgid "Transfer money from your account to one or others"
#~ msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"

View File

@ -97,9 +97,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a> <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
</li> </li>
{% if "treasury.billing"|not_empty_model_change_list %} {% if "treasury.invoice"|not_empty_model_change_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'treasury:billing' %}"><i class="fa fa-money"></i>{% trans 'Treasury' %} </a> <a class="nav-link" href="{% url 'treasury:invoice' %}"><i class="fa fa-money"></i>{% trans 'Treasury' %} </a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -3,7 +3,7 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags pretty_money %} {% load crispy_forms_tags pretty_money %}
{% block content %} {% block content %}
<p><a class="btn btn-default" href="{% url 'treasury:billing' %}">{% trans "Billings list" %}</a></p> <p><a class="btn btn-default" href="{% url 'treasury:invoice' %}">{% trans "Invoices list" %}</a></p>
<form method="post" action=""> <form method="post" action="">
{% csrf_token %} {% csrf_token %}
{% crispy form %} {% crispy form %}
@ -31,7 +31,7 @@
</div> </div>
</div> </div>
</td> </td>
{{ form.billing }} {{ form.invoice }}
{{ form.id }} {{ form.id }}
</tr> </tr>
{% endfor %} {% endfor %}
@ -63,7 +63,7 @@
</div> </div>
</div> </div>
</td> </td>
{{ formset.empty_form.billing }} {{ formset.empty_form.invoice }}
{{ formset.empty_form.id }} {{ formset.empty_form.id }}
</tr> </tr>
</tbody> </tbody>

View File

@ -5,6 +5,6 @@
{% render_table table %} {% render_table table %}
<a class="btn btn-primary" href="{% url 'treasury:billing_create' %}">{% trans "New billing" %}</a> <a class="btn btn-primary" href="{% url 'treasury:invoice_create' %}">{% trans "New invoice" %}</a>
{% endblock %} {% endblock %}