diff --git a/src/pretix/base/email.py b/src/pretix/base/email.py index 7d0252de78..3413fe377c 100644 --- a/src/pretix/base/email.py +++ b/src/pretix/base/email.py @@ -177,7 +177,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer): op.variation, op.subevent, op.attendee_name, - (op.pk if op.addon_to_id else None), + op.addon_to_id, (op.pk if op.has_addons else None) ) )] diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 051888728b..6af6f739a3 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -1330,6 +1330,10 @@ class AbstractPosition(models.Model): else: return {} + @property + def item_and_variation(self): + return self.item, self.variation + @meta_info_data.setter def meta_info_data(self, d): self.meta_info = json.dumps(d) diff --git a/src/pretix/base/templates/pretixbase/email/order_details.html b/src/pretix/base/templates/pretixbase/email/order_details.html index 538bff2f89..6511e15ed0 100644 --- a/src/pretix/base/templates/pretixbase/email/order_details.html +++ b/src/pretix/base/templates/pretixbase/email/order_details.html @@ -90,13 +90,16 @@ {% for groupkey, positions in cart %} - {% if not groupkey.4 %} {# is addon #} + {% if not groupkey.4 %} {# is not addon #} {{ positions|length }}x {% endif %} {% if groupkey.4 %} {# is addon #} + + {% if positions|length > 1 %} + {{ positions|length }}x + {% endif %} {% endif %} {{ groupkey.0.name }}{% if groupkey.1 %} – {{ groupkey.1.value }}{% endif %} {% if groupkey.2 %} {# subevent #} diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html b/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html index 6075e16fb1..dc3fb28528 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html @@ -121,8 +121,9 @@
diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html index 2c3dd117c4..0a39fabfc5 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html @@ -205,7 +205,7 @@ {% endif %} {% elif line.addon_to %} -
 
+
{% if line.count == 1 %} {% else %}{{ line.count }}{% endif %}
{% if event.settings.display_net_prices %} {{ line.net_price|money:event.currency }} diff --git a/src/pretix/presale/views/__init__.py b/src/pretix/presale/views/__init__.py index f841a26af4..8f6b58d543 100644 --- a/src/pretix/presale/views/__init__.py +++ b/src/pretix/presale/views/__init__.py @@ -35,7 +35,7 @@ from collections import defaultdict from datetime import datetime, timedelta from decimal import Decimal -from functools import wraps +from functools import partial, wraps from itertools import groupby from django.conf import settings @@ -145,7 +145,7 @@ class CartMixin: # Group items of the same variation # We do this by list manipulations instead of a GROUP BY query, as # Django is unable to join related models in a .values() query - def keyfunc(pos): + def keyfunc(pos, for_sorting=False): if isinstance(pos, OrderPosition): if pos.addon_to_id: i = pos.addon_to.positionid @@ -167,12 +167,11 @@ class CartMixin: if downloads \ or pos.pk in has_addons \ - or pos.addon_to_id \ or pos.item.issue_giftcard \ or (answers and (has_attendee_data or bool(pos.item.questions.all()))): # do not use .exists() to re-use prefetch cache return ( # standalone positions are grouped by main product position id, addons below them also sorted by position id - i, addon_penalty, pos.pk, + i, addon_penalty, pos.positionid if isinstance(pos, OrderPosition) else pos.pk, # all other places are only used for positions that can be grouped. We just put zeros. ) + (0, ) * 10 @@ -180,13 +179,22 @@ class CartMixin: category_key = (pos.item.category.position, pos.item.category.id) if pos.item.category is not None else (0, 0) item_key = pos.item.position, pos.item_id variation_key = (pos.variation.position, pos.variation.id) if pos.variation is not None else (0, 0) + grp = category_key + item_key + variation_key + (pos.price, (pos.voucher_id or 0), (pos.subevent_id or 0), (pos.seat_id or 0)) + if pos.addon_to_id: + if for_sorting: + ii = pos.positionid if isinstance(pos, OrderPosition) else pos.pk + else: + ii = 0 + return ( + i, addon_penalty, ii, + ) + category_key + item_key + variation_key + (pos.price, (pos.voucher_id or 0), (pos.subevent_id or 0), (pos.seat_id or 0)) return ( # These are grouped by attributes so we don't put any position ids 0, 0, 0, - ) + category_key + item_key + variation_key + (pos.price, (pos.voucher_id or 0), (pos.subevent_id or 0), (pos.seat_id or 0)) + ) + grp positions = [] - for k, g in groupby(sorted(lcp, key=keyfunc), key=keyfunc): + for k, g in groupby(sorted(lcp, key=partial(keyfunc, for_sorting=True)), key=keyfunc): g = list(g) group = g[0] group.count = len(g) diff --git a/src/pretix/static/pretixpresale/scss/_cart.scss b/src/pretix/static/pretixpresale/scss/_cart.scss index 3b5877781b..8c9e18531c 100644 --- a/src/pretix/static/pretixpresale/scss/_cart.scss +++ b/src/pretix/static/pretixpresale/scss/_cart.scss @@ -12,7 +12,7 @@ .count form { display: inline; } - .price, .count, .download-desktop { + .price, .download-desktop { text-align: right; } .price small, @@ -55,6 +55,7 @@ } .count { width: percentage((2 / $grid-columns)); + text-align: center; } .singleprice, .totalprice { width: percentage((3 / $grid-columns));