diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index f2dc8ec76b..aa4f6f9c2e 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -164,54 +164,55 @@ class ItemCategory(LoggedModel): match = set(match.pk for match in self.cross_selling_match_products.only('pk')) # TODO prefetch this return (self.items.all(), {}) if any(pos.item.pk in match for pos in cart) else (None, {}) if self.cross_selling_condition == 'discounts': - potential_discounts_by_cartpos = defaultdict(list) + if not hasattr(self.event, '_potential_discounts_by_item_for_current_cart'): + potential_discounts_by_cartpos = defaultdict(list) - from ..services.pricing import apply_discounts - apply_discounts( - self.event, - sales_channel, - [ - (cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, - cp.listed_price - cp.price_after_voucher) - for cp in cart - ], - collect_potential_discounts=potential_discounts_by_cartpos - ) - - # technically, this is a dict, but we use it as an OrderedSet here - potential_discount_set = dict.fromkeys(info for lst in potential_discounts_by_cartpos.values() for info in lst) - - # sum up the max_counts and pass them on (also pass on the discount_rules so we can calculate actual discounted prices later): - # group by benefit product - # - max_count for product: sum up max_counts - # - discount_rule for product: take first discount_rule - - def discount_info(item, infos_for_item): - infos_for_item = list(infos_for_item) - return ( - item, - sum(max_count for (item, discount_rule, max_count, i) in infos_for_item), - next(discount_rule for (item, discount_rule, max_count, i) in infos_for_item) + from ..services.pricing import apply_discounts + apply_discounts( + self.event, + sales_channel, + [ + (cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, + cp.listed_price - cp.price_after_voucher) + for cp in cart + ], + collect_potential_discounts=potential_discounts_by_cartpos ) - grouped_by_item = [ - discount_info(item, infos_for_item) for item, infos_for_item in - groupby( - sorted( - ( - (item, discount_rule, max_count, i) - for (discount_rule, max_count, i) in potential_discount_set.keys() - for item in discount_rule.benefit_limit_products.all() + # technically, this is a dict, but we use it as an OrderedSet here + potential_discount_set = dict.fromkeys(info for lst in potential_discounts_by_cartpos.values() for info in lst) + + # sum up the max_counts and pass them on (also pass on the discount_rules so we can calculate actual discounted prices later): + # group by benefit product + # - max_count for product: sum up max_counts + # - discount_rule for product: take first discount_rule + + def discount_info(item, infos_for_item): + infos_for_item = list(infos_for_item) + return ( + item, + sum(max_count for (item, discount_rule, max_count, i) in infos_for_item), + next(discount_rule for (item, discount_rule, max_count, i) in infos_for_item) + ) + + self.event._potential_discounts_by_item_for_current_cart = [ + discount_info(item, infos_for_item) for item, infos_for_item in + groupby( + sorted( + ( + (item, discount_rule, max_count, i) + for (discount_rule, max_count, i) in potential_discount_set.keys() + for item in discount_rule.benefit_limit_products.all() + ), + key=lambda tup: tup[0].pk ), - key=lambda tup: tup[0].pk - ), - lambda tup: tup[0]) - ] + lambda tup: tup[0]) + ] my_item_pks = self.items.values_list('pk', flat=True) potential_discount_items = { item.pk: (max_count, discount_rule) - for item, max_count, discount_rule in grouped_by_item + for item, max_count, discount_rule in self.event._potential_discounts_by_item_for_current_cart if max_count > 0 and item.pk in my_item_pks and item.is_available() }