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>
|
||||
<meta charset="UTF-8" />
|
||||
<title>
|
||||
{{ name }} - Liste des commandes
|
||||
{{ grouped_order.name }} - Liste des commandes
|
||||
</title>
|
||||
<style type="text/css">
|
||||
@page {
|
||||
{% if items|length > 6 %}
|
||||
size: letter landscape;
|
||||
size: letter landscape;
|
||||
{% else %}
|
||||
size: A4;
|
||||
size: A4;
|
||||
{% endif %}
|
||||
margin: 2cm 1.5cm;
|
||||
@frame footer {
|
||||
-pdf-frame-content: footer;
|
||||
bottom: 0cm;
|
||||
height: 1.5cm;
|
||||
right: 0cm;
|
||||
width: 10cm;
|
||||
}
|
||||
margin: 2cm 1.5cm;
|
||||
@bottom-right{
|
||||
font-size: 10pt;
|
||||
font-family: sans-serif;
|
||||
content: "Liste générée par la Chariotte - chariotte.fr | Page " counter(page)"/" counter(pages);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px black solid;
|
||||
border-collapse: collapse;
|
||||
border: 1px black solid;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th, td {
|
||||
vertical-align: center;
|
||||
padding: 3px 2px 2px 2px;
|
||||
line-height: 0.8em;
|
||||
vertical-align: center;
|
||||
padding: 3px 2px 2px 2px;
|
||||
border: 1px black solid;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
page-break-inside: avoid;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
page-break-inside: avoid;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: -1em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="text-align: center">
|
||||
{{ name }} - {{ delivery_date }}
|
||||
</h1>
|
||||
<h2 style="text-align: center">
|
||||
{{ grouped_order.name }} - {{ grouped_order.delivery_date }}
|
||||
</h2>
|
||||
{% if items %}
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -69,12 +71,13 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<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 %}
|
||||
<th>
|
||||
<td>
|
||||
{{ item.price }} €
|
||||
</th>
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr style="background-color: #bababa">
|
||||
<th style="text-align: left">TOTAL</th>
|
||||
|
@ -83,21 +86,21 @@
|
|||
{{ item.ordered_nb }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
<th>{{ total_price }} €</th>
|
||||
<th>{{ grouped_order.total_price }} €</th>
|
||||
</tr>
|
||||
{% for order, ordered_items in orders_dict.items %}
|
||||
<tr>
|
||||
<th>
|
||||
<td>
|
||||
{{ order.author }}
|
||||
</th>
|
||||
</td>
|
||||
{% for ordered_item in ordered_items %}
|
||||
<th>
|
||||
<td>
|
||||
{{ ordered_item }}
|
||||
</th>
|
||||
</td>
|
||||
{% endfor %}
|
||||
<th>
|
||||
<td>
|
||||
{{ order.price }} €
|
||||
</th>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -106,11 +109,5 @@
|
|||
Aucun produit n'a été commandé
|
||||
{% endif %}
|
||||
|
||||
<div id="footer">
|
||||
Liste générée par la Chariotte - chariotte.fr | Page
|
||||
<pdf:pagenumber />
|
||||
/
|
||||
<pdf:pagecount />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1445,7 +1445,7 @@ class TestItemCreateView:
|
|||
|
||||
class TestGroupedOrderSheetView:
|
||||
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"""
|
||||
grouped_order = create_grouped_order(
|
||||
days_before_delivery_date=5,
|
||||
|
@ -1497,17 +1497,18 @@ class TestGroupedOrderSheetView:
|
|||
)
|
||||
response = client_log.get(generate_sheet_url)
|
||||
assert response.status_code == 200
|
||||
assert response.context["name"] == "gr order test"
|
||||
assert response.context["delivery_date"] == grouped_order.delivery_date
|
||||
assert response.context["grouped_order"] == grouped_order
|
||||
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)
|
||||
response = client_log.get(generate_sheet_url)
|
||||
assert response.status_code == 200
|
||||
assert response.context["name"] == "gr order test"
|
||||
assert response.context["delivery_date"] == grouped_order.delivery_date
|
||||
assert response.context["grouped_order"] == grouped_order
|
||||
assert response.context["items"].count() == 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:
|
||||
|
|
|
@ -52,7 +52,7 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
"<int:pk>/gerer/imprimer",
|
||||
views.GroupedOrderSheetView.as_view(),
|
||||
views.DownloadGroupedOrderSheetView.as_view(),
|
||||
name="grouped_order_sheet",
|
||||
),
|
||||
path(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# fmt: off
|
||||
from .grouped_order import (ExportGroupedOrderEmailAddressesToCSVView,
|
||||
from .grouped_order import (DownloadGroupedOrderSheetView,
|
||||
ExportGroupedOrderEmailAddressesToCSVView,
|
||||
GroupedOrderAddItemsView, GroupedOrderCreateView,
|
||||
GroupedOrderDeleteView, GroupedOrderDetailView,
|
||||
GroupedOrderDuplicateView, GroupedOrderOverview,
|
||||
|
|
|
@ -8,6 +8,8 @@ from django.template.loader import get_template
|
|||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.views import generic
|
||||
from django_weasyprint import WeasyTemplateResponseMixin
|
||||
from django_weasyprint.views import WeasyTemplateResponse, WeasyTemplateView
|
||||
from xhtml2pdf import pisa
|
||||
|
||||
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
|
||||
|
||||
|
||||
class GroupedOrderSheetMixin:
|
||||
"""Mixin for grouped order info export (pdf sheet and csv)"""
|
||||
class GroupedOrderSheetView(UserPassesTestMixin, generic.DetailView):
|
||||
"""View for gathering information about the groupedorder, in order to download it
|
||||
in the DownloadGroupedOrderSheetView"""
|
||||
|
||||
def get_grouped_order_infos(self, grouped_order_id):
|
||||
grouped_order = get_object_or_404(GroupedOrder, id=grouped_order_id)
|
||||
model = GroupedOrder
|
||||
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
|
||||
items = grouped_order.item_set.filter(ordered_nb__gt=0).order_by("name")
|
||||
|
@ -238,30 +250,19 @@ class GroupedOrderSheetMixin:
|
|||
ordered_values.append(value)
|
||||
orders_dict[order] = ordered_values # order as key of the dict
|
||||
|
||||
return {
|
||||
"name": grouped_order.name,
|
||||
"delivery_date": grouped_order.delivery_date,
|
||||
"items": items,
|
||||
"orders_dict": orders_dict,
|
||||
"total_price": grouped_order.total_price,
|
||||
}
|
||||
context["items"] = items
|
||||
context["orders_dict"] = orders_dict
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class GroupedOrderSheetView(GroupedOrderOverview, GroupedOrderSheetMixin):
|
||||
"""View to get a pdf sheet summing up the grouped order for delivery"""
|
||||
class DownloadGroupedOrderSheetView(WeasyTemplateResponseMixin, GroupedOrderSheetView):
|
||||
"""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(self, request, pk):
|
||||
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")
|
||||
def get_pdf_filename(self):
|
||||
# filename for download
|
||||
return f"{self.get_object().delivery_date} - {self.get_object().name}"
|
||||
|
||||
|
||||
class ExportGroupedOrderEmailAddressesToCSVView(UserPassesTestMixin, generic.View):
|
||||
|
|
|
@ -100,5 +100,7 @@ webencodings==0.5.1
|
|||
# cssselect2
|
||||
# html5lib
|
||||
# tinycss2
|
||||
weasyprint==58.0 #django-weasyprint doesn't work with weasyprint 59.0
|
||||
django-weasyprint==2.2.0
|
||||
xhtml2pdf==0.2.11
|
||||
# via la-chariotte (pyproject.toml)
|
||||
|
|
Loading…
Reference in a new issue