formatting, refactoring

This commit is contained in:
Mira Weller
2024-07-18 21:26:09 +02:00
parent 9c3150ccde
commit e18c699529
5 changed files with 78 additions and 45 deletions

View File

@@ -304,8 +304,8 @@ class Discount(LoggedModel):
# unused discount ("for each 1 ticket you buy, get 50% on 2 t-shirts", cart content: 1 ticket # unused discount ("for each 1 ticket you buy, get 50% on 2 t-shirts", cart content: 1 ticket
# but 0 t-shirts) -> 2 shirt maybe potential discount (if the 1 ticket is not consumed by a later discount) # but 0 t-shirts) -> 2 shirt maybe potential discount (if the 1 ticket is not consumed by a later discount)
for i, idx in enumerate(condition_idx_group[ for i, idx in enumerate(condition_idx_group[
n_groups * self.condition_min_count: n_groups * self.condition_min_count:
possible_applications_cond * self.condition_min_count possible_applications_cond * self.condition_min_count
]): ]):
print(i, idx) print(i, idx)
collect_potential_discounts[idx] += [ collect_potential_discounts[idx] += [

View File

@@ -1,12 +1,12 @@
from collections import defaultdict from collections import defaultdict
from decimal import Decimal from decimal import Decimal
from itertools import groupby from itertools import groupby
from math import inf
from typing import List from typing import List
from django.utils.functional import cached_property from django.utils.functional import cached_property
from math import inf
from pretix.base.models import ItemCategory, CartPosition, SalesChannel from pretix.base.models import CartPosition, ItemCategory, SalesChannel
from pretix.presale.views.event import get_grouped_items from pretix.presale.views.event import get_grouped_items
@@ -163,8 +163,8 @@ class CrossSellingService:
item.original_price = item.original_price or item.display_price item.original_price = item.original_price or item.display_price
previous_price = item.display_price previous_price = item.display_price
new_price = ( new_price = (
previous_price * ( previous_price * (
(Decimal('100.00') - discount_rule.benefit_discount_matching_percent) / Decimal('100.00')) (Decimal('100.00') - discount_rule.benefit_discount_matching_percent) / Decimal('100.00'))
) )
item.display_price = new_price item.display_price = new_price
@@ -177,4 +177,3 @@ class CrossSellingService:
new_items.append(item) new_items.append(item)
return new_items return new_items

View File

@@ -31,7 +31,7 @@ from pretix.base.models import (
AbstractPosition, InvoiceAddress, Item, ItemAddOn, ItemVariation, AbstractPosition, InvoiceAddress, Item, ItemAddOn, ItemVariation,
SalesChannel, Voucher, SalesChannel, Voucher,
) )
from pretix.base.models.discount import PositionInfo, Discount from pretix.base.models.discount import Discount, PositionInfo
from pretix.base.models.event import Event, SubEvent from pretix.base.models.event import Event, SubEvent
from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule
from pretix.base.timemachine import time_machine_now from pretix.base.timemachine import time_machine_now

View File

@@ -515,7 +515,6 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
return request._checkoutflow_addons_applicable return request._checkoutflow_addons_applicable
def is_completed(self, request, warn=False): def is_completed(self, request, warn=False):
if getattr(self, '_completed', None) is not None: if getattr(self, '_completed', None) is not None:
return self._completed return self._completed

View File

@@ -28,7 +28,7 @@ import pytest
from django.utils.timezone import now from django.utils.timezone import now
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from pretix.base.models import Discount, Event, Organizer, CartPosition from pretix.base.models import CartPosition, Discount, Event, Organizer
from pretix.base.services.cross_selling import CrossSellingService from pretix.base.services.cross_selling import CrossSellingService
@@ -41,32 +41,50 @@ def event():
) )
return event return event
def pattern(regex, **kwargs): def pattern(regex, **kwargs):
return re.compile(regex), kwargs return re.compile(regex), kwargs
cond_suffix = [
pattern(r" in the same subevent$", subevent_mode=Discount.SUBEVENT_MODE_SAME),
pattern(r" in distinct subevents$", subevent_mode=Discount.SUBEVENT_MODE_DISTINCT),
]
cond_patterns = [
pattern(r"^Buy at least (?P<condition_min_count>\d+) of (?P<condition_limit_products>.+)$",
condition_all_products=False),
pattern(r"^Buy at least (?P<condition_min_count>\d+) products$",
condition_all_products=True),
pattern(r"^Spend at least (?P<condition_min_value>\d+)\$$",
condition_all_products=True),
pattern(r"^For every (?P<condition_min_count>\d+) of (?P<condition_limit_products>.+)$",
condition_all_products=False),
pattern(r"^For every (?P<condition_min_count>\d+) products$",
condition_all_products=True),
]
benefit_patterns = [
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on them\.$",
benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on everything\.$",
benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on "
r"(?P<benefit_only_apply_to_cheapest_n_matches>\d+) of them\.$",
benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on "
r"(?P<benefit_only_apply_to_cheapest_n_matches>\d+) of (?P<benefit_limit_products>.+)\.$",
benefit_same_products=False),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on "
r"(?P<benefit_limit_products>.+)\.$",
benefit_same_products=False),
]
def make_discount(description, event: Event): def make_discount(description, event: Event):
cond_suffix = [
pattern(r" in the same subevent$", subevent_mode=Discount.SUBEVENT_MODE_SAME),
pattern(r" in distinct subevents$", subevent_mode=Discount.SUBEVENT_MODE_DISTINCT),
]
cond_patterns = [
pattern(r"^Buy at least (?P<condition_min_count>\d+) of (?P<condition_limit_products>.+)$", condition_all_products=False),
pattern(r"^Buy at least (?P<condition_min_count>\d+) products$", condition_all_products=True),
pattern(r"^Spend at least (?P<condition_min_value>\d+)\$$", condition_all_products=True),
pattern(r"^For every (?P<condition_min_count>\d+) of (?P<condition_limit_products>.+)$", condition_all_products=False),
pattern(r"^For every (?P<condition_min_count>\d+) products$", condition_all_products=True),
]
benefit_patterns = [
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on them\.$", benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on everything\.$", benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on (?P<benefit_only_apply_to_cheapest_n_matches>\d+) of them\.$", benefit_same_products=True),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on (?P<benefit_only_apply_to_cheapest_n_matches>\d+) of (?P<benefit_limit_products>.+)\.$", benefit_same_products=False),
pattern(r"^get (?P<benefit_discount_matching_percent>\d+)% discount on (?P<benefit_limit_products>.+)\.$", benefit_same_products=False),
]
condition, benefit = description.split(', ') condition, benefit = description.split(', ')
d = Discount(event=event, internal_name=description) d = Discount(event=event, internal_name=description)
d.save() d.save()
def apply(patterns: List[Tuple[re.Pattern, dict]], input): def apply(patterns: List[Tuple[re.Pattern, dict]], input):
for regex, options in patterns: for regex, options in patterns:
m = regex.search(input) m = regex.search(input)
@@ -224,7 +242,8 @@ def check_cart_behaviour(event, cart_contents, recommendations):
positions = [ positions = [
CartPosition( CartPosition(
item_id=event.items.get(name=item_name).pk, item_id=event.items.get(name=item_name).pk,
subevent_id=1, line_price_gross=Decimal(regular_price), addon_to=None, is_bundled=False, listed_price=Decimal(regular_price), price_after_voucher=Decimal(regular_price) subevent_id=1, line_price_gross=Decimal(regular_price), addon_to=None, is_bundled=False,
listed_price=Decimal(regular_price), price_after_voucher=Decimal(regular_price)
) for (item_name, regular_price, expected_discounted_price) in cart_contents ) for (item_name, regular_price, expected_discounted_price) in cart_contents
] ]
expected_recommendations = split_table(recommendations) expected_recommendations = split_table(recommendations)
@@ -232,13 +251,15 @@ def check_cart_behaviour(event, cart_contents, recommendations):
service = CrossSellingService(event, event.organizer.sales_channels.get(identifier='web'), positions, None) service = CrossSellingService(event, event.organizer.sales_channels.get(identifier='web'), positions, None)
result = service.get_data() result = service.get_data()
result_recommendations = [ result_recommendations = [
[str(category.name), str(item.name), str(item.original_price.gross.quantize(Decimal('0.00'))), str(item.display_price.gross.quantize(Decimal('0.00'))), str(item.order_max)] [str(category.name), str(item.name), str(item.original_price.gross.quantize(Decimal('0.00'))),
str(item.display_price.gross.quantize(Decimal('0.00'))), str(item.order_max)]
for category, items in result for category, items in result
for item in items for item in items
] ]
assert result_recommendations == expected_recommendations assert result_recommendations == expected_recommendations
assert [str(price) for price, discount in service._discounted_prices] == [disc for (name, reg, disc) in cart_contents] assert [str(price) for price, discount in service._discounted_prices] == [
expected_discounted_price for (item_name, regular_price, expected_discounted_price) in cart_contents]
@scopes_disabled() @scopes_disabled()
@@ -250,7 +271,8 @@ def test_2f1r_discount_cross_selling(event):
) )
make_discount('For every 2 of Regular Ticket, get 50% discount on 1 of Reduced Ticket.', event) make_discount('For every 2 of Regular Ticket, get 50% discount on 1 of Reduced Ticket.', event)
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -259,7 +281,8 @@ def test_2f1r_discount_cross_selling(event):
Tickets Reduced Ticket 23.00 11.50 1 Tickets Reduced Ticket 23.00 11.50 1
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -269,7 +292,8 @@ def test_2f1r_discount_cross_selling(event):
recommendations=''' Price Discounted Price Max Count recommendations=''' Price Discounted Price Max Count
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -280,7 +304,8 @@ def test_2f1r_discount_cross_selling(event):
recommendations=''' Price Discounted Price Max Count recommendations=''' Price Discounted Price Max Count
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -291,7 +316,8 @@ def test_2f1r_discount_cross_selling(event):
Tickets Reduced Ticket 23.00 11.50 2 Tickets Reduced Ticket 23.00 11.50 2
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -304,7 +330,9 @@ def test_2f1r_discount_cross_selling(event):
Tickets Reduced Ticket 23.00 11.50 1 Tickets Reduced Ticket 23.00 11.50 1
''' '''
) )
check_cart_behaviour(event, print("The interesting part:")
check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -318,7 +346,8 @@ def test_2f1r_discount_cross_selling(event):
Tickets Reduced Ticket 23.00 11.50 1 Tickets Reduced Ticket 23.00 11.50 1
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -348,7 +377,8 @@ def test_free_drinks(event):
) )
make_discount('Spend at least 100$, get 100% discount on 1 of Free Drinks.', event) make_discount('Spend at least 100$, get 100% discount on 1 of Free Drinks.', event)
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -356,7 +386,8 @@ def test_free_drinks(event):
recommendations=''' Price Discounted Price Max Count recommendations=''' Price Discounted Price Max Count
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -366,7 +397,8 @@ def test_free_drinks(event):
Free Drinks Free Drinks 50.00 0.00 1 Free Drinks Free Drinks 50.00 0.00 1
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -388,7 +420,8 @@ def test_five_tickets_one_free(event):
# we don't expect a recommendation here, as in the current implementation we only recommend based on discounts # we don't expect a recommendation here, as in the current implementation we only recommend based on discounts
# where the condition is already completely satisfied but no (or not enough) benefitting products are in the # where the condition is already completely satisfied but no (or not enough) benefitting products are in the
# cart yet # cart yet
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -398,7 +431,8 @@ def test_five_tickets_one_free(event):
recommendations=''' Price Discounted Price Max Count recommendations=''' Price Discounted Price Max Count
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
@@ -409,7 +443,8 @@ def test_five_tickets_one_free(event):
recommendations=''' Price Discounted Price Max Count recommendations=''' Price Discounted Price Max Count
''' '''
) )
check_cart_behaviour(event, check_cart_behaviour(
event,
cart_contents=''' Price Discounted cart_contents=''' Price Discounted
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00
Regular Ticket 42.00 42.00 Regular Ticket 42.00 42.00