better order_max handling

This commit is contained in:
Mira Weller
2024-06-13 15:48:10 +02:00
parent b136ac37c8
commit cadf8dd39d
3 changed files with 22 additions and 14 deletions

View File

@@ -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]

View File

@@ -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

View File

@@ -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