From 5ee62c551e15b041202c116b31043fe56414b436 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 21 Nov 2022 15:47:57 +0100 Subject: [PATCH] Group identical lines on invoice PDF (#2918) --- src/pretix/base/invoice.py | 41 ++++++++++++++++++++-------- src/pretix/base/services/invoices.py | 22 ++++++++------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/pretix/base/invoice.py b/src/pretix/base/invoice.py index 679ccdc5a..867b6f8c8 100644 --- a/src/pretix/base/invoice.py +++ b/src/pretix/base/invoice.py @@ -23,6 +23,7 @@ import logging from collections import defaultdict from decimal import Decimal from io import BytesIO +from itertools import groupby from typing import Tuple import bleach @@ -554,31 +555,47 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer): pgettext('invoice', 'Amount'), )] + def _group_key(line): + return (line.description, line.tax_rate, line.tax_name, line.net_value, line.gross_value, line.subevent_id, + line.event_date_from, line.event_date_to) + total = Decimal('0.00') - for line in self.invoice.lines.all(): + for (description, tax_rate, tax_name, net_value, gross_value, *ignored), lines in groupby(self.invoice.lines.all(), key=_group_key): + lines = list(lines) if has_taxes: + if len(lines) > 1: + single_price_line = pgettext('invoice', 'Single price: {net_price} net / {gross_price} gross').format( + net_price=money_filter(net_value, self.invoice.event.currency), + gross_price=money_filter(gross_value, self.invoice.event.currency), + ) + description = description + "\n" + single_price_line tdata.append(( Paragraph( - bleach.clean(line.description, tags=['br']).strip().replace('
', '
').replace('\n', '
\n'), + bleach.clean(description, tags=['br']).strip().replace('
', '
').replace('\n', '
\n'), self.stylesheet['Normal'] ), - "1", - localize(line.tax_rate) + " %", - money_filter(line.net_value, self.invoice.event.currency), - money_filter(line.gross_value, self.invoice.event.currency), + str(len(lines)), + localize(tax_rate) + " %", + money_filter(net_value * len(lines), self.invoice.event.currency), + money_filter(gross_value * len(lines), self.invoice.event.currency), )) else: + if len(lines) > 1: + single_price_line = pgettext('invoice', 'Single price: {price}').format( + price=money_filter(gross_value, self.invoice.event.currency), + ) + description = description + "\n" + single_price_line tdata.append(( Paragraph( - bleach.clean(line.description, tags=['br']).strip().replace('
', '
').replace('\n', '
\n'), + bleach.clean(description, tags=['br']).strip().replace('
', '
').replace('\n', '
\n'), self.stylesheet['Normal'] ), - "1", - money_filter(line.gross_value, self.invoice.event.currency), + str(len(lines)), + money_filter(gross_value * len(lines), self.invoice.event.currency), )) - taxvalue_map[line.tax_rate, line.tax_name] += line.tax_value - grossvalue_map[line.tax_rate, line.tax_name] += line.gross_value - total += line.gross_value + taxvalue_map[tax_rate, tax_name] += (gross_value - net_value) * len(lines) + grossvalue_map[tax_rate, tax_name] += gross_value * len(lines) + total += gross_value * len(lines) if has_taxes: tdata.append([ diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index c004f7079..aefb4af8f 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -452,17 +452,19 @@ def build_preview_invoice_pdf(event): if event.tax_rules.exists(): for i, tr in enumerate(event.tax_rules.all()): - tax = tr.tax(Decimal('100.00'), base_price_is='gross') - InvoiceLine.objects.create( - invoice=invoice, description=_("Sample product {}").format(i + 1), - gross_value=tax.gross, tax_value=tax.tax, - tax_rate=tax.rate - ) + for j in range(150): + tax = tr.tax(Decimal('100.00'), base_price_is='gross') + InvoiceLine.objects.create( + invoice=invoice, description=_("Sample product {}").format(i + 1), + gross_value=tax.gross, tax_value=tax.tax, + tax_rate=tax.rate + ) else: - InvoiceLine.objects.create( - invoice=invoice, description=_("Sample product A"), - gross_value=100, tax_value=0, tax_rate=0 - ) + for i in range(150): + InvoiceLine.objects.create( + invoice=invoice, description=_("Sample product A"), + gross_value=100, tax_value=0, tax_rate=0 + ) return event.invoice_renderer.generate(invoice)