mirror of
https://github.com/pretix/pretix.git
synced 2026-05-08 15:44:02 +00:00
formatting, refactoring
This commit is contained in:
@@ -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] += [
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -421,4 +456,4 @@ def test_five_tickets_one_free(event):
|
|||||||
''',
|
''',
|
||||||
recommendations=''' Price Discounted Price Max Count
|
recommendations=''' Price Discounted Price Max Count
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user