generate pdf list with weasyprint

This commit is contained in:
Laetitia Getti 2023-07-20 14:01:39 +02:00
parent a6267ab711
commit 7d1fd312f4
6 changed files with 80 additions and 78 deletions

View file

@ -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>

View file

@ -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:

View file

@ -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(

View file

@ -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,

View file

@ -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):

View file

@ -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)