From cadf8dd39d87b51cdd23be867ef2be3ac12e5b2d Mon Sep 17 00:00:00 2001 From: Mira Weller Date: Thu, 13 Jun 2024 15:48:10 +0200 Subject: [PATCH] better order_max handling --- src/pretix/base/models/discount.py | 6 +++--- src/pretix/base/models/items.py | 27 +++++++++++++++++---------- src/pretix/presale/checkoutflow.py | 3 ++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/pretix/base/models/discount.py b/src/pretix/base/models/discount.py index ea58e511e..ac4df6e71 100644 --- a/src/pretix/base/models/discount.py +++ b/src/pretix/base/models/discount.py @@ -23,7 +23,7 @@ from collections import defaultdict from decimal import Decimal from itertools import groupby -from math import ceil +from math import ceil, inf from typing import Dict, Optional, Tuple from django.core.exceptions import ValidationError @@ -268,7 +268,7 @@ class Discount(LoggedModel): if collect_potential_discounts is not None: for idx in condition_idx_group: - collect_potential_discounts[idx] = [(self, None, -1)] + collect_potential_discounts[idx] = [(self, inf, -1)] def _apply_min_count(self, positions, condition_idx_group, benefit_idx_group, result, collect_potential_discounts): if len(condition_idx_group) < self.condition_min_count: @@ -321,7 +321,7 @@ class Discount(LoggedModel): if collect_potential_discounts is not None: for idx in consume_idx: - collect_potential_discounts[idx] = [(self, None, -1)] + collect_potential_discounts[idx] = [(self, inf, -1)] for idx in benefit_idx: previous_price = positions[idx][LINE_PRICE_GROSS] diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index a6b6dad82..ff7394600 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -64,6 +64,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy from django_countries.fields import Country from django_scopes import ScopedManager from i18nfield.fields import I18nCharField, I18nTextField +from math import inf from pretix.base.models.base import LoggedModel from pretix.base.models.fields import MultiStringField @@ -160,6 +161,7 @@ class ItemCategory(LoggedModel): if self.cross_selling_condition == 'always': return self.items.all(), {} if self.cross_selling_condition == 'products': + # TODO set max_count for products with max_per_order 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 ([], {}) if self.cross_selling_condition == 'discounts': @@ -183,8 +185,19 @@ class ItemCategory(LoggedModel): # - 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, + min( + sum(max_count for (item, discount_rule, max_count, i) in infos_for_item), + (item.max_per_order - sum(1 for pos in cart if pos.item_id == item.pk)) if item.max_per_order else inf + ), + next(discount_rule for (item, discount_rule, max_count, i) in infos_for_item) + ) + grouped_by_item = [ - (item, list(infos_for_item)) for item, infos_for_item in + discount_info(item, infos_for_item) for item, infos_for_item in groupby( sorted( ( @@ -197,17 +210,11 @@ class ItemCategory(LoggedModel): lambda tup: tup[0]) ] - def sum_or_none(iter): - return functools.reduce(lambda x, y: None if x is None or y is None else x + y, iter, 0) - my_item_pks = self.items.values_list('pk', flat=True) potential_discount_items = { - item.pk: ( - sum_or_none(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) - ) - for item, infos_for_item in grouped_by_item - if item.pk in my_item_pks + item.pk: (max_count, discount_rule) + for item, max_count, discount_rule in grouped_by_item + if max_count > 0 and item.pk in my_item_pks and item.is_available() } return self.items.filter(pk__in=potential_discount_items), potential_discount_items diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index a22e6e56d..dd55740d3 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -54,6 +54,7 @@ from django.utils.translation import ( ) from django.views.generic.base import TemplateResponseMixin from django_scopes import scopes_disabled +from math import inf from pretix.base.models import Customer, Membership, Order from pretix.base.models.items import Question @@ -665,7 +666,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep): (max_count, discount_rule) = discount_info[item.pk] # set item.order_max for benefit_only_apply_to_cheapest_n_matches discounted items - if max_count: + if max_count and max_count != inf: item.order_max = min(item.order_max, max_count) # calculate discounted price