Group identical add-ons in cart (#2500)

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2022-03-01 15:49:16 +01:00
committed by GitHub
parent a9b0651345
commit b74f5508b7
7 changed files with 29 additions and 12 deletions

View File

@@ -177,7 +177,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
op.variation, op.variation,
op.subevent, op.subevent,
op.attendee_name, op.attendee_name,
(op.pk if op.addon_to_id else None), op.addon_to_id,
(op.pk if op.has_addons else None) (op.pk if op.has_addons else None)
) )
)] )]

View File

@@ -1330,6 +1330,10 @@ class AbstractPosition(models.Model):
else: else:
return {} return {}
@property
def item_and_variation(self):
return self.item, self.variation
@meta_info_data.setter @meta_info_data.setter
def meta_info_data(self, d): def meta_info_data(self, d):
self.meta_info = json.dumps(d) self.meta_info = json.dumps(d)

View File

@@ -90,13 +90,16 @@
{% for groupkey, positions in cart %} {% for groupkey, positions in cart %}
<tr> <tr>
<td> <td>
{% if not groupkey.4 %} {# is addon #} {% if not groupkey.4 %} {# is not addon #}
{{ positions|length }}x {{ positions|length }}x
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if groupkey.4 %} {# is addon #} {% if groupkey.4 %} {# is addon #}
+ +
{% if positions|length > 1 %}
{{ positions|length }}x
{% endif %}
{% endif %} {% endif %}
{{ groupkey.0.name }}{% if groupkey.1 %} {{ groupkey.1.value }}{% endif %} {{ groupkey.0.name }}{% if groupkey.1 %} {{ groupkey.1.value }}{% endif %}
{% if groupkey.2 %} {# subevent #} {% if groupkey.2 %} {# subevent #}

View File

@@ -121,8 +121,9 @@
</label> </label>
<div class="col-md-9 form-control-text"> <div class="col-md-9 form-control-text">
<ul class="addon-list"> <ul class="addon-list">
{% for a in pos.addons.all %} {% regroup pos.addons.all by item_and_variation as addons_by_itemvar %}
<li>{{ a.item.name }}{% if a.variation %} {{ a.variation.value }}{% endif %}</li> {% for group in addons_by_itemvar %}
<li>{% if group.list|length > 1 %}{{ group.list|length }}&times; {% endif %}{{ group.grouper.0.name }}{% if group.grouper.1 %} {{ group.grouper.1.value }}{% endif %}</li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>

View File

@@ -205,7 +205,7 @@
{% endif %} {% endif %}
</div> </div>
{% elif line.addon_to %} {% elif line.addon_to %}
<div role="cell" class="count">&nbsp;</div> <div role="cell" class="count">{% if line.count == 1 %}&nbsp;{% else %}{{ line.count }}{% endif %}</div>
<div role="cell" class="singleprice price"> <div role="cell" class="singleprice price">
{% if event.settings.display_net_prices %} {% if event.settings.display_net_prices %}
{{ line.net_price|money:event.currency }} {{ line.net_price|money:event.currency }}

View File

@@ -35,7 +35,7 @@
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from decimal import Decimal from decimal import Decimal
from functools import wraps from functools import partial, wraps
from itertools import groupby from itertools import groupby
from django.conf import settings from django.conf import settings
@@ -145,7 +145,7 @@ class CartMixin:
# Group items of the same variation # Group items of the same variation
# We do this by list manipulations instead of a GROUP BY query, as # We do this by list manipulations instead of a GROUP BY query, as
# Django is unable to join related models in a .values() query # 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 isinstance(pos, OrderPosition):
if pos.addon_to_id: if pos.addon_to_id:
i = pos.addon_to.positionid i = pos.addon_to.positionid
@@ -167,12 +167,11 @@ class CartMixin:
if downloads \ if downloads \
or pos.pk in has_addons \ or pos.pk in has_addons \
or pos.addon_to_id \
or pos.item.issue_giftcard \ 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 or (answers and (has_attendee_data or bool(pos.item.questions.all()))): # do not use .exists() to re-use prefetch cache
return ( return (
# standalone positions are grouped by main product position id, addons below them also sorted by position id # 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. # all other places are only used for positions that can be grouped. We just put zeros.
) + (0, ) * 10 ) + (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) 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 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) 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 ( return (
# These are grouped by attributes so we don't put any position ids # These are grouped by attributes so we don't put any position ids
0, 0, 0, 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 = [] 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) g = list(g)
group = g[0] group = g[0]
group.count = len(g) group.count = len(g)

View File

@@ -12,7 +12,7 @@
.count form { .count form {
display: inline; display: inline;
} }
.price, .count, .download-desktop { .price, .download-desktop {
text-align: right; text-align: right;
} }
.price small, .price small,
@@ -55,6 +55,7 @@
} }
.count { .count {
width: percentage((2 / $grid-columns)); width: percentage((2 / $grid-columns));
text-align: center;
} }
.singleprice, .totalprice { .singleprice, .totalprice {
width: percentage((3 / $grid-columns)); width: percentage((3 / $grid-columns));