invoicing: start pdf invoice generation (#75729) #50
|
@ -19,6 +19,7 @@ Depends: python3-distutils,
|
|||
python3-gadjo,
|
||||
python3-publik-django-templatetags,
|
||||
python3-requests,
|
||||
weasyprint,
|
||||
${misc:Depends},
|
||||
${python3:Depends},
|
||||
Recommends: python3-django-mellon
|
||||
|
|
|
@ -358,7 +358,9 @@ class AbstractInvoice(models.Model):
|
|||
|
||||
|
||||
class DraftInvoice(AbstractInvoice):
|
||||
pass
|
||||
@property
|
||||
def formatted_number(self):
|
||||
return '%s-%s' % (_('TEMPORARY'), self.pk)
|
||||
|
||||
|
||||
class Counter(models.Model):
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
{% load static i18n %}
|
||||
<!doctype html><html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>{{ invoice.formatted_number }}</title>
|
||||
<meta name="author" content="Entr'ouvert">
|
||||
<style>
|
||||
html {
|
||||
color: #14213d;
|
||||
font-family: Source Sans Pro;
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1ee494;
|
||||
font-family: Pacifico;
|
||||
font-size: 40pt;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
aside {
|
||||
display: flex;
|
||||
margin: 2em 0 4em;
|
||||
}
|
||||
aside address {
|
||||
font-style: normal;
|
||||
white-space: pre-line;
|
||||
}
|
||||
aside address#from {
|
||||
color: #a9a;
|
||||
flex: 1;
|
||||
}
|
||||
aside address#to {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
dl {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: right;
|
||||
top: 0;
|
||||
}
|
||||
dt, dd {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
}
|
||||
dt {
|
||||
color: #a9a;
|
||||
}
|
||||
dt::before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
dt::after {
|
||||
content: ':';
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
th {
|
||||
border-bottom: .2mm solid #a9a;
|
||||
color: #a9a;
|
||||
font-size: 10pt;
|
||||
font-weight: 400;
|
||||
padding-bottom: .25cm;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
td {
|
||||
padding-top: 7mm;
|
||||
}
|
||||
td:last-of-type {
|
||||
color: #1ee494;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
th, td {
|
||||
text-align: center;
|
||||
}
|
||||
th:first-of-type, td:first-of-type {
|
||||
text-align: left;
|
||||
}
|
||||
th:last-of-type, td:last-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
table#total {
|
||||
background: #f6f6f6;
|
||||
border-color: #f6f6f6;
|
||||
border-style: solid;
|
||||
border-width: 2cm 3cm;
|
||||
bottom: 0;
|
||||
font-size: 20pt;
|
||||
margin: 0 -3cm;
|
||||
position: absolute;
|
||||
width: 18cm;
|
||||
}
|
||||
div.break {
|
||||
break-before: page;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{% trans "Invoice" %}</h1>
|
||||
<aside>
|
||||
<address id="from">
|
||||
{{ regie.label }}
|
||||
</address>
|
||||
<address id="to">
|
||||
{{ invoice.payer_first_name}} {{ invoice.payer_last_name}}
|
||||
</address>
|
||||
</aside>
|
||||
<dl id="informations">
|
||||
<dt>{% trans "Invoice number" %}</dt>
|
||||
<dd>{{ invoice.formatted_number }}</dd>
|
||||
<dt>{% trans "Date" %}</dt>
|
||||
<dd>{{ invoice.created_at|date }}</dd>
|
||||
</dl>
|
||||
<table id="lines">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% trans "Beneficiary" %}</th>
|
||||
<th>{% trans "Price" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% trans "Subtotal" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for line in lines %}
|
||||
<tr>
|
||||
<td>{{ line.label }}</td>
|
||||
<td>{{ line.user_first_name}} {{ line.user_last_name}}</td>
|
||||
<td>{{ line.unit_amount }}</td>
|
||||
<td>{{ line.quantity }}</td>
|
||||
<td>{{ line.total_amount }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="total">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Payment deadline" %}</th>
|
||||
<th>{% trans "Total due" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ invoice.date_payment_deadline }}</td>
|
||||
<td>{{ invoice.total_amount }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -29,6 +29,7 @@
|
|||
{% url 'lingo-manager-invoicing-pool-detail' regie_pk=regie.pk pk=invoice.pool.campaign_id pool_pk=invoice.pool.pk as pool_url %}
|
||||
<li class="invoice untoggled" data-invoice-id="{{ invoice.pk }}" data-invoice-lines-url="{% url 'lingo-manager-invoicing-regie-invoice-line-list' regie_pk=regie.pk invoice_pk=invoice.pk %}">
|
||||
{% blocktrans with invoice_number=invoice.formatted_number payer_id=invoice.payer_external_id payer_name=invoice.payer_name payer_demat=invoice.payer_demat|yesno payer_direct_debit=invoice.payer_direct_debit|yesno amount=invoice.total_amount number=invoice.formatted_number %}Invoice <a href="{{ pool_url }}?number={{ number }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer_id }}">{{ payer_name }} ({{ payer_id }})</a>, amount {{ amount }}€, demat: {{ payer_demat }}, direct debit: {{ payer_direct_debit }}{% endblocktrans %}
|
||||
- <a href="{% url 'lingo-manager-invoicing-regie-invoice-pdf' regie_pk=regie.pk invoice_pk=invoice.pk %}">{% trans "download" %}</a>
|
||||
<span class="togglable"></span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -49,10 +49,11 @@
|
|||
{% for invoice in object_list %}
|
||||
<li class="invoice untoggled" data-invoice-id="{{ invoice.pk }}" data-invoice-lines-url="{% url 'lingo-manager-invoicing-invoice-line-list' regie_pk=regie.pk pk=object.pk pool_pk=pool.pk invoice_pk=invoice.pk %}">
|
||||
{% if pool.draft %}
|
||||
{% blocktrans with number=invoice.pk payer_id=invoice.payer_external_id payer_name=invoice.payer_name payer_demat=invoice.payer_demat|yesno payer_direct_debit=invoice.payer_direct_debit|yesno amount=invoice.total_amount prefix=_('TEMPORARY')%}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">{{ prefix }}-{{ number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer_id }}">{{ payer_name }} ({{ payer_id }})</a>, amount {{ amount }}€, demat: {{ payer_demat }}, direct debit: {{ payer_direct_debit }}{% endblocktrans %}
|
||||
{% blocktrans with invoice_number=invoice.formatted_number payer_id=invoice.payer_external_id payer_name=invoice.payer_name payer_demat=invoice.payer_demat|yesno payer_direct_debit=invoice.payer_direct_debit|yesno amount=invoice.total_amount number=invoice.pk %}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer_id }}">{{ payer_name }} ({{ payer_id }})</a>, amount {{ amount }}€, demat: {{ payer_demat }}, direct debit: {{ payer_direct_debit }}{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with invoice_number=invoice.formatted_number payer_id=invoice.payer_external_id payer_name=invoice.payer_name payer_demat=invoice.payer_demat|yesno payer_direct_debit=invoice.payer_direct_debit|yesno amount=invoice.total_amount number=invoice.formatted_number %}Invoice <a href="{{ journal_url }}?invoice_number={{ number }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer_id }}">{{ payer_name }} ({{ payer_id }})</a>, amount {{ amount }}€, demat: {{ payer_demat }}, direct debit: {{ payer_direct_debit }}{% endblocktrans %}
|
||||
{% endif %}
|
||||
- <a href="{% url 'lingo-manager-invoicing-invoice-pdf' regie_pk=regie.pk pk=invoice.pool.campaign_id pool_pk=invoice.pool.pk invoice_pk=invoice.pk %}">{% trans "download" %}</a>
|
||||
<span class="togglable"></span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -98,6 +98,11 @@ urlpatterns = [
|
|||
views.pool_delete,
|
||||
name='lingo-manager-invoicing-pool-delete',
|
||||
),
|
||||
path(
|
||||
'regie/<int:regie_pk>/campaign/<int:pk>/pool/<int:pool_pk>/invoice/<int:invoice_pk>/pdf/',
|
||||
views.invoice_pdf,
|
||||
name='lingo-manager-invoicing-invoice-pdf',
|
||||
),
|
||||
path(
|
||||
'ajax/regie/<int:regie_pk>/campaign/<int:pk>/pool/<int:pool_pk>/invoice/<int:invoice_pk>/lines/',
|
||||
views.invoice_line_list,
|
||||
|
@ -118,6 +123,11 @@ urlpatterns = [
|
|||
views.regie_invoice_list,
|
||||
name='lingo-manager-invoicing-regie-invoice-list',
|
||||
),
|
||||
path(
|
||||
'regie/<int:regie_pk>/invoice/<int:invoice_pk>/pdf/',
|
||||
views.regie_invoice_pdf,
|
||||
name='lingo-manager-invoicing-regie-invoice-pdf',
|
||||
),
|
||||
path(
|
||||
'ajax/regie/<int:regie_pk>/invoice/<int:invoice_pk>/lines/',
|
||||
views.regie_invoice_line_list,
|
||||
|
|
|
@ -24,6 +24,7 @@ from django.db.models import CharField, Count, IntegerField, JSONField, OuterRef
|
|||
from django.db.models.functions import Coalesce
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.loader import get_template
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext
|
||||
|
@ -36,6 +37,7 @@ from django.views.generic import (
|
|||
TemplateView,
|
||||
UpdateView,
|
||||
)
|
||||
from weasyprint import HTML
|
||||
|
||||
from lingo.agendas.chrono import ChronoError, unlock_events_check
|
||||
from lingo.agendas.models import Agenda
|
||||
|
@ -616,6 +618,50 @@ class PoolDeleteView(DeleteView):
|
|||
pool_delete = PoolDeleteView.as_view()
|
||||
|
||||
|
||||
class PDFMixin:
|
||||
def html(self):
|
||||
template = get_template(self.template_name)
|
||||
context = {
|
||||
'regie': self.regie,
|
||||
'invoice': self.object,
|
||||
'lines': self.object.lines.all().order_by('user_external_id', 'event_date', 'pk'),
|
||||
}
|
||||
return template.render(context)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
result = self.html()
|
||||
if 'html' in request.GET:
|
||||
return HttpResponse(result)
|
||||
html = HTML(string=result)
|
||||
pdf = html.write_pdf()
|
||||
response = HttpResponse(pdf, content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % self.object.formatted_number
|
||||
return response
|
||||
|
||||
|
||||
class InvoicePDFView(PDFMixin, DetailView):
|
||||
template_name = 'lingo/invoicing/invoice.html'
|
||||
pk_url_kwarg = 'invoice_pk'
|
||||
model = Invoice
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
||||
self.pool = get_object_or_404(
|
||||
Pool, pk=kwargs['pool_pk'], campaign=kwargs['pk'], campaign__regie=self.regie
|
||||
)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
invoice_model = Invoice
|
||||
if self.pool.draft:
|
||||
invoice_model = DraftInvoice
|
||||
return invoice_model.objects.filter(pool=self.pool)
|
||||
|
||||
|
||||
invoice_pdf = InvoicePDFView.as_view()
|
||||
|
||||
|
||||
class InvoiceLineListView(ListView):
|
||||
template_name = 'lingo/invoicing/manager_invoice_lines.html'
|
||||
|
||||
|
@ -791,6 +837,22 @@ class RegieInvoiceListView(ListView):
|
|||
regie_invoice_list = RegieInvoiceListView.as_view()
|
||||
|
||||
|
||||
class RegieInvoicePDFView(PDFMixin, DetailView):
|
||||
template_name = 'lingo/invoicing/invoice.html'
|
||||
pk_url_kwarg = 'invoice_pk'
|
||||
model = Invoice
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.regie = get_object_or_404(Regie, pk=kwargs['regie_pk'])
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(regie=self.regie)
|
||||
|
||||
|
||||
regie_invoice_pdf = RegieInvoicePDFView.as_view()
|
||||
|
||||
|
||||
class RegieInvoiceLineListView(ListView):
|
||||
template_name = 'lingo/invoicing/manager_invoice_lines.html'
|
||||
|
||||
|
|
1
setup.py
1
setup.py
|
@ -166,6 +166,7 @@ setup(
|
|||
'eopayment>=1.60',
|
||||
'djangorestframework>=3.3, <3.13',
|
||||
'django-filter',
|
||||
'weasyprint',
|
||||
],
|
||||
zip_safe=False,
|
||||
cmdclass={
|
||||
|
|
|
@ -3,6 +3,8 @@ import datetime
|
|||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import localtime
|
||||
|
||||
from lingo.agendas.chrono import ChronoError
|
||||
from lingo.agendas.models import Agenda
|
||||
|
@ -1211,13 +1213,13 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
if draft:
|
||||
assert (
|
||||
resp.pyquery('li[data-invoice-id="%s"]' % invoice1.pk).text()
|
||||
== 'Invoice TEMPORARY-%s addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no'
|
||||
== 'Invoice TEMPORARY-%s addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no - download'
|
||||
% invoice1.pk
|
||||
)
|
||||
else:
|
||||
assert resp.pyquery(
|
||||
'li[data-invoice-id="%s"]' % invoice1.pk
|
||||
).text() == 'Invoice F%02s-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no' % (
|
||||
).text() == 'Invoice F%02d-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no - download' % (
|
||||
regie.pk,
|
||||
invoice1.created_at.strftime('%y-%m'),
|
||||
)
|
||||
|
@ -1245,13 +1247,13 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
if draft:
|
||||
assert (
|
||||
resp.pyquery('li[data-invoice-id="%s"]' % invoice2.pk).text()
|
||||
== 'Invoice TEMPORARY-%s addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes'
|
||||
== 'Invoice TEMPORARY-%s addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes - download'
|
||||
% invoice2.pk
|
||||
)
|
||||
else:
|
||||
assert resp.pyquery(
|
||||
'li[data-invoice-id="%s"]' % invoice2.pk
|
||||
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes' % (
|
||||
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes - download' % (
|
||||
regie.pk,
|
||||
invoice2.created_at.strftime('%y-%m'),
|
||||
)
|
||||
|
@ -1359,6 +1361,164 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
assert len(resp.pyquery('li.invoice')) == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize('draft', [True, False])
|
||||
def test_invoice_pdf(app, admin_user, draft):
|
||||
invoice_model = DraftInvoice if draft else Invoice
|
||||
line_model = DraftInvoiceLine if draft else InvoiceLine
|
||||
|
||||
regie = Regie.objects.create(label='Foo')
|
||||
campaign = Campaign.objects.create(
|
||||
regie=regie,
|
||||
date_start=datetime.date(2022, 9, 1),
|
||||
date_end=datetime.date(2022, 10, 1),
|
||||
date_publication=datetime.date(2022, 10, 1),
|
||||
date_payment_deadline=datetime.date(2022, 10, 31),
|
||||
date_issue=datetime.date(2022, 10, 31),
|
||||
date_debit=datetime.date(2022, 11, 15),
|
||||
)
|
||||
pool = Pool.objects.create(
|
||||
campaign=campaign,
|
||||
draft=draft,
|
||||
status='completed',
|
||||
)
|
||||
invoice = invoice_model.objects.create(
|
||||
date_publication=campaign.date_publication,
|
||||
date_payment_deadline=campaign.date_payment_deadline,
|
||||
date_issue=campaign.date_issue,
|
||||
regie=regie,
|
||||
pool=pool,
|
||||
payer_external_id='payer:1',
|
||||
payer_first_name='First1',
|
||||
payer_last_name='Name1',
|
||||
payer_demat=True,
|
||||
payer_direct_debit=False,
|
||||
)
|
||||
if not draft:
|
||||
invoice.set_number()
|
||||
invoice.save()
|
||||
|
||||
line_model.objects.create(
|
||||
event_date=datetime.date(2022, 9, 1),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=1,
|
||||
total_amount=1,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 11',
|
||||
user_external_id='user:1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
line_model.objects.create(
|
||||
event_date=datetime.date(2022, 9, 2),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=2,
|
||||
total_amount=2,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 12',
|
||||
user_external_id='user:2',
|
||||
user_first_name='User2',
|
||||
user_last_name='Name2',
|
||||
)
|
||||
line_model.objects.create(
|
||||
event_date=datetime.date(2022, 9, 3),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=3,
|
||||
total_amount=3,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 13',
|
||||
user_external_id='user:1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, campaign.pk, pool.pk, invoice.pk)
|
||||
)
|
||||
assert resp.pyquery('address#from').text() == 'Foo'
|
||||
assert resp.pyquery('address#to').text() == 'First1 Name1'
|
||||
if draft:
|
||||
assert resp.pyquery('dl#informations').text() == 'Invoice number\nTEMPORARY-%s\nDate\n%s' % (
|
||||
invoice.pk,
|
||||
date_format(localtime(invoice.created_at), 'DATE_FORMAT'),
|
||||
)
|
||||
else:
|
||||
assert resp.pyquery('dl#informations').text() == 'Invoice number\nF%02d-%s-0000001\nDate\n%s' % (
|
||||
regie.pk,
|
||||
invoice.created_at.strftime('%y-%m'),
|
||||
date_format(localtime(invoice.created_at), 'DATE_FORMAT'),
|
||||
)
|
||||
assert len(resp.pyquery('table#lines tbody tr')) == 3
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(1)').text() == 'Label 11\nUser1 Name1\n1.00\n1.0\n1.00'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(2)').text() == 'Label 13\nUser1 Name1\n3.00\n1.0\n3.00'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(3)').text() == 'Label 12\nUser2 Name2\n2.00\n1.0\n2.00'
|
||||
)
|
||||
assert resp.pyquery('table#total tbody').text() == 'Oct. 31, 2022\n6.00'
|
||||
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (0, campaign.pk, pool.pk, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, 0, pool.pk, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, campaign.pk, 0, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, campaign.pk, pool.pk, 0),
|
||||
status=404,
|
||||
)
|
||||
other_regie = Regie.objects.create(label='Foo')
|
||||
other_campaign = Campaign.objects.create(
|
||||
regie=other_regie,
|
||||
date_start=datetime.date(2022, 9, 1),
|
||||
date_end=datetime.date(2022, 10, 1),
|
||||
date_publication=datetime.date(2022, 10, 1),
|
||||
date_payment_deadline=datetime.date(2022, 10, 31),
|
||||
date_issue=datetime.date(2022, 10, 31),
|
||||
date_debit=datetime.date(2022, 11, 15),
|
||||
)
|
||||
other_pool = Pool.objects.create(
|
||||
campaign=other_campaign,
|
||||
draft=draft,
|
||||
status='completed',
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (other_regie.pk, campaign.pk, pool.pk, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, other_campaign.pk, pool.pk, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/invoice/%s/pdf/?html'
|
||||
% (regie.pk, campaign.pk, other_pool.pk, invoice.pk),
|
||||
status=404,
|
||||
)
|
||||
|
||||
|
||||
def test_journal_pool(app, admin_user):
|
||||
regie = Regie.objects.create(label='Foo')
|
||||
campaign = Campaign.objects.create(
|
||||
|
|
|
@ -5,6 +5,8 @@ from urllib.parse import urlparse
|
|||
import pytest
|
||||
from django.contrib.auth.models import Group
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import localtime
|
||||
from webtest import Upload
|
||||
|
||||
from lingo.agendas.models import Agenda
|
||||
|
@ -628,7 +630,7 @@ def test_regie_invoices(app, admin_user):
|
|||
assert '#%s' % orphan_line.pk not in resp
|
||||
assert resp.pyquery(
|
||||
'li[data-invoice-id="%s"]' % invoice1.pk
|
||||
).text() == 'Invoice F%02s-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no' % (
|
||||
).text() == 'Invoice F%02d-%s-0000001 addressed to First1 Name1 (payer:1), amount 6.00€, demat: yes, direct debit: no - download' % (
|
||||
regie.pk,
|
||||
invoice1.created_at.strftime('%y-%m'),
|
||||
)
|
||||
|
@ -653,7 +655,7 @@ def test_regie_invoices(app, admin_user):
|
|||
)
|
||||
assert resp.pyquery(
|
||||
'li[data-invoice-id="%s"]' % invoice2.pk
|
||||
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes' % (
|
||||
).text() == 'Invoice F%02d-%s-0000002 addressed to First2 Name2 (payer:2), amount 1.00€, demat: no, direct debit: yes - download' % (
|
||||
regie.pk,
|
||||
invoice2.created_at.strftime('%y-%m'),
|
||||
)
|
||||
|
@ -750,3 +752,103 @@ def test_regie_invoices(app, admin_user):
|
|||
params={'user_last_name': 'name1'},
|
||||
)
|
||||
assert len(resp.pyquery('li.invoice')) == 2
|
||||
|
||||
|
||||
def test_regie_invoice_pdf(app, admin_user):
|
||||
regie = Regie.objects.create(label='Foo')
|
||||
campaign = Campaign.objects.create(
|
||||
regie=regie,
|
||||
date_start=datetime.date(2022, 9, 1),
|
||||
date_end=datetime.date(2022, 10, 1),
|
||||
date_publication=datetime.date(2022, 10, 1),
|
||||
date_payment_deadline=datetime.date(2022, 10, 31),
|
||||
date_issue=datetime.date(2022, 10, 31),
|
||||
date_debit=datetime.date(2022, 11, 15),
|
||||
)
|
||||
pool = Pool.objects.create(
|
||||
campaign=campaign,
|
||||
draft=False,
|
||||
status='completed',
|
||||
)
|
||||
invoice = Invoice.objects.create(
|
||||
date_publication=campaign.date_publication,
|
||||
date_payment_deadline=campaign.date_payment_deadline,
|
||||
date_issue=campaign.date_issue,
|
||||
regie=regie,
|
||||
pool=pool,
|
||||
payer_external_id='payer:1',
|
||||
payer_first_name='First1',
|
||||
payer_last_name='Name1',
|
||||
payer_demat=True,
|
||||
payer_direct_debit=False,
|
||||
)
|
||||
invoice.set_number()
|
||||
invoice.save()
|
||||
|
||||
InvoiceLine.objects.create(
|
||||
event_date=datetime.date(2022, 9, 1),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=1,
|
||||
total_amount=1,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 11',
|
||||
user_external_id='user:1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
InvoiceLine.objects.create(
|
||||
event_date=datetime.date(2022, 9, 2),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=2,
|
||||
total_amount=2,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 12',
|
||||
user_external_id='user:2',
|
||||
user_first_name='User2',
|
||||
user_last_name='Name2',
|
||||
)
|
||||
InvoiceLine.objects.create(
|
||||
event_date=datetime.date(2022, 9, 3),
|
||||
invoice=invoice,
|
||||
quantity=1,
|
||||
unit_amount=3,
|
||||
total_amount=3,
|
||||
status='success',
|
||||
pool=pool,
|
||||
label='Label 13',
|
||||
user_external_id='user:1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/invoicing/regie/%s/invoice/%s/pdf/?html' % (regie.pk, invoice.pk))
|
||||
assert resp.pyquery('address#from').text() == 'Foo'
|
||||
assert resp.pyquery('address#to').text() == 'First1 Name1'
|
||||
assert resp.pyquery('dl#informations').text() == 'Invoice number\nF%02d-%s-0000001\nDate\n%s' % (
|
||||
regie.pk,
|
||||
invoice.created_at.strftime('%y-%m'),
|
||||
date_format(localtime(invoice.created_at), 'DATE_FORMAT'),
|
||||
)
|
||||
assert len(resp.pyquery('table#lines tbody tr')) == 3
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(1)').text() == 'Label 11\nUser1 Name1\n1.00\n1.0\n1.00'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(2)').text() == 'Label 13\nUser1 Name1\n3.00\n1.0\n3.00'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('table#lines tbody tr:nth-child(3)').text() == 'Label 12\nUser2 Name2\n2.00\n1.0\n2.00'
|
||||
)
|
||||
assert resp.pyquery('table#total tbody').text() == 'Oct. 31, 2022\n6.00'
|
||||
|
||||
resp = app.get('/manage/invoicing/regie/%s//invoice/%s/pdf/?html' % (0, invoice.pk), status=404)
|
||||
resp = app.get('/manage/invoicing/regie/%s//invoice/%s/pdf/?html' % (regie.pk, 0), status=404)
|
||||
other_regie = Regie.objects.create(label='Foo')
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/invoice/%s/pdf/?html' % (other_regie.pk, invoice.pk), status=404
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue