mirror of
https://framagit.org/la-chariotte/la-chariotte.git
synced 2025-05-01 19:32:26 +02:00
generate pdf list with weasyprint
This commit is contained in:
parent
a6267ab711
commit
7d1fd312f4
6 changed files with 80 additions and 78 deletions
|
@ -3,57 +3,59 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>
|
<title>
|
||||||
{{ name }} - Liste des commandes
|
{{ grouped_order.name }} - Liste des commandes
|
||||||
</title>
|
</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@page {
|
@page {
|
||||||
{% if items|length > 6 %}
|
{% if items|length > 6 %}
|
||||||
size: letter landscape;
|
size: letter landscape;
|
||||||
{% else %}
|
{% else %}
|
||||||
size: A4;
|
size: A4;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
margin: 2cm 1.5cm;
|
margin: 2cm 1.5cm;
|
||||||
@frame footer {
|
@bottom-right{
|
||||||
-pdf-frame-content: footer;
|
font-size: 10pt;
|
||||||
bottom: 0cm;
|
font-family: sans-serif;
|
||||||
height: 1.5cm;
|
content: "Liste générée par la Chariotte - chariotte.fr | Page " counter(page)"/" counter(pages);
|
||||||
right: 0cm;
|
}
|
||||||
width: 10cm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 11pt;
|
font-size: 11pt;
|
||||||
}
|
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
border: 1px black solid;
|
border: 1px black solid;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
vertical-align: center;
|
vertical-align: center;
|
||||||
padding: 3px 2px 2px 2px;
|
padding: 3px 2px 2px 2px;
|
||||||
line-height: 0.8em;
|
border: 1px black solid;
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
text-align: center;
|
page-break-inside: avoid;
|
||||||
page-break-inside: avoid;
|
word-break: break-all;
|
||||||
word-break: break-all;
|
word-wrap: break-word;
|
||||||
word-wrap: break-word;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: -1em;
|
||||||
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 style="text-align: center">
|
<h2 style="text-align: center">
|
||||||
{{ name }} - {{ delivery_date }}
|
{{ grouped_order.name }} - {{ grouped_order.delivery_date }}
|
||||||
</h1>
|
</h2>
|
||||||
{% if items %}
|
{% if items %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -69,12 +71,13 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr style="background-color: #bababa">
|
<tr style="background-color: #bababa">
|
||||||
<th style="text-align: left">Prix unitaire</th>
|
<td style="text-align: left">Prix unitaire</td>
|
||||||
{% for item in items %}
|
{% for item in items %}
|
||||||
<th>
|
<td>
|
||||||
{{ item.price }} €
|
{{ item.price }} €
|
||||||
</th>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="background-color: #bababa">
|
<tr style="background-color: #bababa">
|
||||||
<th style="text-align: left">TOTAL</th>
|
<th style="text-align: left">TOTAL</th>
|
||||||
|
@ -83,21 +86,21 @@
|
||||||
{{ item.ordered_nb }}
|
{{ item.ordered_nb }}
|
||||||
</th>
|
</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th>{{ total_price }} €</th>
|
<th>{{ grouped_order.total_price }} €</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for order, ordered_items in orders_dict.items %}
|
{% for order, ordered_items in orders_dict.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<td>
|
||||||
{{ order.author }}
|
{{ order.author }}
|
||||||
</th>
|
</td>
|
||||||
{% for ordered_item in ordered_items %}
|
{% for ordered_item in ordered_items %}
|
||||||
<th>
|
<td>
|
||||||
{{ ordered_item }}
|
{{ ordered_item }}
|
||||||
</th>
|
</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th>
|
<td>
|
||||||
{{ order.price }} €
|
{{ order.price }} €
|
||||||
</th>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -106,11 +109,5 @@
|
||||||
Aucun produit n'a été commandé
|
Aucun produit n'a été commandé
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div id="footer">
|
|
||||||
Liste générée par la Chariotte - chariotte.fr | Page
|
|
||||||
<pdf:pagenumber />
|
|
||||||
/
|
|
||||||
<pdf:pagecount />
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1445,7 +1445,7 @@ class TestItemCreateView:
|
||||||
|
|
||||||
class TestGroupedOrderSheetView:
|
class TestGroupedOrderSheetView:
|
||||||
def test_get__not_orga(self, client_log, other_user):
|
def test_get__not_orga(self, client_log, other_user):
|
||||||
"""A user that is not organiszer of the GO accesses the sheet view.
|
"""A user that is not organizer of the GO accesses the sheet view.
|
||||||
They get a 403 error"""
|
They get a 403 error"""
|
||||||
grouped_order = create_grouped_order(
|
grouped_order = create_grouped_order(
|
||||||
days_before_delivery_date=5,
|
days_before_delivery_date=5,
|
||||||
|
@ -1497,17 +1497,18 @@ class TestGroupedOrderSheetView:
|
||||||
)
|
)
|
||||||
response = client_log.get(generate_sheet_url)
|
response = client_log.get(generate_sheet_url)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.context["name"] == "gr order test"
|
assert response.context["grouped_order"] == grouped_order
|
||||||
assert response.context["delivery_date"] == grouped_order.delivery_date
|
|
||||||
assert response.context["items"].count() == 0
|
assert response.context["items"].count() == 0
|
||||||
|
assert len(response.context["orders_dict"]) == 0
|
||||||
|
|
||||||
|
# we order some items in the grouped order
|
||||||
order = order_items_in_grouped_order(grouped_order)
|
order = order_items_in_grouped_order(grouped_order)
|
||||||
response = client_log.get(generate_sheet_url)
|
response = client_log.get(generate_sheet_url)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.context["name"] == "gr order test"
|
assert response.context["grouped_order"] == grouped_order
|
||||||
assert response.context["delivery_date"] == grouped_order.delivery_date
|
|
||||||
assert response.context["items"].count() == 2
|
assert response.context["items"].count() == 2
|
||||||
assert response.context["orders_dict"][order] == [3, 2]
|
assert response.context["orders_dict"][order] == [3, 2]
|
||||||
assert response.context["total_price"] == 24
|
assert response.context["grouped_order"].total_price == 24
|
||||||
|
|
||||||
|
|
||||||
class TestExportGroupedOrderEmailAddressesToCSVView:
|
class TestExportGroupedOrderEmailAddressesToCSVView:
|
||||||
|
|
|
@ -52,7 +52,7 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/gerer/imprimer",
|
"<int:pk>/gerer/imprimer",
|
||||||
views.GroupedOrderSheetView.as_view(),
|
views.DownloadGroupedOrderSheetView.as_view(),
|
||||||
name="grouped_order_sheet",
|
name="grouped_order_sheet",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# fmt: off
|
# fmt: off
|
||||||
from .grouped_order import (ExportGroupedOrderEmailAddressesToCSVView,
|
from .grouped_order import (DownloadGroupedOrderSheetView,
|
||||||
|
ExportGroupedOrderEmailAddressesToCSVView,
|
||||||
GroupedOrderAddItemsView, GroupedOrderCreateView,
|
GroupedOrderAddItemsView, GroupedOrderCreateView,
|
||||||
GroupedOrderDeleteView, GroupedOrderDetailView,
|
GroupedOrderDeleteView, GroupedOrderDetailView,
|
||||||
GroupedOrderDuplicateView, GroupedOrderOverview,
|
GroupedOrderDuplicateView, GroupedOrderOverview,
|
||||||
|
|
|
@ -8,6 +8,8 @@ from django.template.loader import get_template
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
from django_weasyprint import WeasyTemplateResponseMixin
|
||||||
|
from django_weasyprint.views import WeasyTemplateResponse, WeasyTemplateView
|
||||||
from xhtml2pdf import pisa
|
from xhtml2pdf import pisa
|
||||||
|
|
||||||
from ..forms import GroupedOrderForm, Item
|
from ..forms import GroupedOrderForm, Item
|
||||||
|
@ -217,11 +219,21 @@ class GroupedOrderAddItemsView(UserPassesTestMixin, generic.ListView):
|
||||||
return GroupedOrder.objects.get(pk=grouped_order_id).orga == self.request.user
|
return GroupedOrder.objects.get(pk=grouped_order_id).orga == self.request.user
|
||||||
|
|
||||||
|
|
||||||
class GroupedOrderSheetMixin:
|
class GroupedOrderSheetView(UserPassesTestMixin, generic.DetailView):
|
||||||
"""Mixin for grouped order info export (pdf sheet and csv)"""
|
"""View for gathering information about the groupedorder, in order to download it
|
||||||
|
in the DownloadGroupedOrderSheetView"""
|
||||||
|
|
||||||
def get_grouped_order_infos(self, grouped_order_id):
|
model = GroupedOrder
|
||||||
grouped_order = get_object_or_404(GroupedOrder, id=grouped_order_id)
|
template_name = "order/grouped_order_sheet.html"
|
||||||
|
context_object_name = "grouped_order"
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
"""Accessible only if the requesting user is the grouped order organizer"""
|
||||||
|
return self.get_object().orga == self.request.user
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(GroupedOrderSheetView, self).get_context_data(**kwargs)
|
||||||
|
grouped_order = self.get_object()
|
||||||
|
|
||||||
# Get ordered items
|
# Get ordered items
|
||||||
items = grouped_order.item_set.filter(ordered_nb__gt=0).order_by("name")
|
items = grouped_order.item_set.filter(ordered_nb__gt=0).order_by("name")
|
||||||
|
@ -238,30 +250,19 @@ class GroupedOrderSheetMixin:
|
||||||
ordered_values.append(value)
|
ordered_values.append(value)
|
||||||
orders_dict[order] = ordered_values # order as key of the dict
|
orders_dict[order] = ordered_values # order as key of the dict
|
||||||
|
|
||||||
return {
|
context["items"] = items
|
||||||
"name": grouped_order.name,
|
context["orders_dict"] = orders_dict
|
||||||
"delivery_date": grouped_order.delivery_date,
|
|
||||||
"items": items,
|
return context
|
||||||
"orders_dict": orders_dict,
|
|
||||||
"total_price": grouped_order.total_price,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class GroupedOrderSheetView(GroupedOrderOverview, GroupedOrderSheetMixin):
|
class DownloadGroupedOrderSheetView(WeasyTemplateResponseMixin, GroupedOrderSheetView):
|
||||||
"""View to get a pdf sheet summing up the grouped order for delivery"""
|
"""View for downloading the grouped order sheet,
|
||||||
|
using django-weasyprint : https://pypi.org/project/django-weasyprint/"""
|
||||||
|
|
||||||
template_name = "order/grouped_order_sheet.html"
|
def get_pdf_filename(self):
|
||||||
|
# filename for download
|
||||||
def get(self, request, pk):
|
return f"{self.get_object().delivery_date} - {self.get_object().name}"
|
||||||
grouped_order_sheet_info = self.get_grouped_order_infos(pk)
|
|
||||||
# should_include_prices = True if request.GET.get("with_prices", False) else False
|
|
||||||
# delivery_sheet_info["include_prices"] = should_include_prices - à ajouter plus tard
|
|
||||||
template = get_template(self.template_name)
|
|
||||||
html = template.render(grouped_order_sheet_info)
|
|
||||||
|
|
||||||
result = BytesIO()
|
|
||||||
pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result)
|
|
||||||
return http.HttpResponse(result.getvalue(), content_type="application/pdf")
|
|
||||||
|
|
||||||
|
|
||||||
class ExportGroupedOrderEmailAddressesToCSVView(UserPassesTestMixin, generic.View):
|
class ExportGroupedOrderEmailAddressesToCSVView(UserPassesTestMixin, generic.View):
|
||||||
|
|
|
@ -100,5 +100,7 @@ webencodings==0.5.1
|
||||||
# cssselect2
|
# cssselect2
|
||||||
# html5lib
|
# html5lib
|
||||||
# tinycss2
|
# tinycss2
|
||||||
|
weasyprint==58.0 #django-weasyprint doesn't work with weasyprint 59.0
|
||||||
|
django-weasyprint==2.2.0
|
||||||
xhtml2pdf==0.2.11
|
xhtml2pdf==0.2.11
|
||||||
# via la-chariotte (pyproject.toml)
|
# via la-chariotte (pyproject.toml)
|
||||||
|
|
Loading…
Reference in a new issue