forked from CGM_Public/pretix_original
Compare commits
2 Commits
master
...
invoice-gr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcf15cbac1 | ||
|
|
b554fc1ad5 |
@@ -62,6 +62,63 @@ from pretix.presale.style import get_fonts
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def addon_aware_groupby(iterable, key, is_addon):
|
||||||
|
"""
|
||||||
|
We use groupby() to visually group identical lines on an invoice. For example, instead of
|
||||||
|
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
Product 2 7.00 EUR
|
||||||
|
|
||||||
|
We want to print
|
||||||
|
|
||||||
|
3x Product 1 5.00 EUR = 15.00 EUR
|
||||||
|
Product 2 7.00 EUR
|
||||||
|
|
||||||
|
However, this fails for setups with addon-products since groupby() only groups consecutive
|
||||||
|
lines with the same identity. So in
|
||||||
|
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
+ Addon 1 2.00 EUR
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
+ Addon 1 2.00 EUR
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
+ Addon 2 3.00 EUR
|
||||||
|
|
||||||
|
There is no consecutive repetition of the same entity. This function provides a specialised groupby which
|
||||||
|
understands the product/addon relationship and packs groups of these addons together if they are, in fact,
|
||||||
|
identical groups:
|
||||||
|
|
||||||
|
2x Product 1 5.00 EUR = 10.00 EUR
|
||||||
|
+ 2x Addon 1 2.00 EUR = 4.00 EUR
|
||||||
|
Product 1 5.00 EUR
|
||||||
|
+ Addon 2 3.00 EUR
|
||||||
|
"""
|
||||||
|
packed_groups = []
|
||||||
|
|
||||||
|
for i in iterable:
|
||||||
|
if is_addon(i):
|
||||||
|
packed_groups[-1][1].append(i)
|
||||||
|
else:
|
||||||
|
packed_groups.append((i, []))
|
||||||
|
# Each packed_groups element contains (parent product, list of addon products)
|
||||||
|
|
||||||
|
def _reorder(packed_groups):
|
||||||
|
# Emit the products as individual products again, reordered by "all parent products, then all addon products"
|
||||||
|
# within each group.
|
||||||
|
for group_grouper, grouped in groupby(packed_groups, key=lambda g: key(g[0]) + tuple(key(a) for a in g[1])):
|
||||||
|
grouped = list(grouped)
|
||||||
|
for grp in grouped:
|
||||||
|
yield grp[0]
|
||||||
|
for i in range(max(len(grp[1]) for grp in grouped)):
|
||||||
|
for grp in grouped:
|
||||||
|
if len(grp[1]) > i:
|
||||||
|
yield grp[1][i]
|
||||||
|
|
||||||
|
return groupby(_reorder(packed_groups), key)
|
||||||
|
|
||||||
|
|
||||||
class NumberedCanvas(Canvas):
|
class NumberedCanvas(Canvas):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.font_regular = kwargs.pop('font_regular')
|
self.font_regular = kwargs.pop('font_regular')
|
||||||
@@ -635,7 +692,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
line.event_date_from, line.event_date_to)
|
line.event_date_from, line.event_date_to)
|
||||||
|
|
||||||
total = Decimal('0.00')
|
total = Decimal('0.00')
|
||||||
for (description, tax_rate, tax_name, net_value, gross_value, *ignored), lines in groupby(self.invoice.lines.all(), key=_group_key):
|
for (description, tax_rate, tax_name, net_value, gross_value, *ignored), lines in addon_aware_groupby(
|
||||||
|
self.invoice.lines.all(),
|
||||||
|
key=_group_key,
|
||||||
|
is_addon=lambda l: l.description.startswith(" +"),
|
||||||
|
):
|
||||||
lines = list(lines)
|
lines = list(lines)
|
||||||
if has_taxes:
|
if has_taxes:
|
||||||
if len(lines) > 1:
|
if len(lines) > 1:
|
||||||
|
|||||||
@@ -38,10 +38,12 @@ from decimal import Decimal
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import DatabaseError, transaction
|
from django.db import DatabaseError, transaction
|
||||||
|
from django.utils.itercompat import is_iterable
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django_countries.fields import Country
|
from django_countries.fields import Country
|
||||||
from django_scopes import scope, scopes_disabled
|
from django_scopes import scope, scopes_disabled
|
||||||
|
|
||||||
|
from pretix.base.invoice import addon_aware_groupby
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
Event, ExchangeRate, Invoice, InvoiceAddress, Item, ItemVariation, Order,
|
Event, ExchangeRate, Invoice, InvoiceAddress, Item, ItemVariation, Order,
|
||||||
OrderPosition, Organizer,
|
OrderPosition, Organizer,
|
||||||
@@ -606,3 +608,55 @@ def test_sales_channels_qualify(env):
|
|||||||
|
|
||||||
event.settings.set('invoice_generate_sales_channels', [])
|
event.settings.set('invoice_generate_sales_channels', [])
|
||||||
assert invoice_qualified(order) is False
|
assert invoice_qualified(order) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_addon_aware_groupby():
|
||||||
|
def is_addon(item):
|
||||||
|
is_addon, id, price = item
|
||||||
|
return is_addon
|
||||||
|
|
||||||
|
def key(item):
|
||||||
|
return item
|
||||||
|
|
||||||
|
def listify(it):
|
||||||
|
return [listify(i) if is_iterable(i) else i for i in it]
|
||||||
|
|
||||||
|
assert listify(addon_aware_groupby([
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(False, 2, 7.00),
|
||||||
|
], key, is_addon)) == [
|
||||||
|
[[False, 1, 5.00], [
|
||||||
|
[False, 1, 5.00],
|
||||||
|
[False, 1, 5.00],
|
||||||
|
[False, 1, 5.00],
|
||||||
|
]],
|
||||||
|
[[False, 2, 7.00], [
|
||||||
|
[False, 2, 7.00],
|
||||||
|
]],
|
||||||
|
]
|
||||||
|
|
||||||
|
assert listify(addon_aware_groupby([
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(True, 101, 2.00),
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(True, 101, 2.00),
|
||||||
|
(False, 1, 5.00),
|
||||||
|
(True, 102, 3.00),
|
||||||
|
], key, is_addon)) == [
|
||||||
|
[[False, 1, 5.00], [
|
||||||
|
[False, 1, 5.00],
|
||||||
|
[False, 1, 5.00],
|
||||||
|
]],
|
||||||
|
[[True, 101, 2.00], [
|
||||||
|
[True, 101, 2.00],
|
||||||
|
[True, 101, 2.00],
|
||||||
|
]],
|
||||||
|
[[False, 1, 5.00], [
|
||||||
|
[False, 1, 5.00],
|
||||||
|
]],
|
||||||
|
[[True, 102, 3.00], [
|
||||||
|
[True, 102, 3.00],
|
||||||
|
]],
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user