Discounts: Add applicability date ranges (Z#23151897) (#4927)

* Add event date fields, add preliminary range check

* Remove function, use filtered queryset for subevent id limit

* Improve and fix date range check

* Add formfields

* Add tests

* Improve tests

* Add new fields to API and documentation

* Add migration

* Change description according to suggestion

* Change discount apply signature, remove unnecessary query

* Rename new fields, simplify range check

* Rename fields in template

* Apply suggestions from code review

Co-authored-by: Raphael Michel <michel@rami.io>

---------

Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
Phin Wolkwitz
2025-03-27 15:36:20 +01:00
committed by GitHub
parent 1a1948e3fa
commit 0b8a7349c7
13 changed files with 412 additions and 274 deletions

View File

@@ -35,6 +35,10 @@ subevent_mode strings Determines h
``"same"`` (discount is only applied for groups within ``"same"`` (discount is only applied for groups within
the same date), or ``"distinct"`` (discount is only applied the same date), or ``"distinct"`` (discount is only applied
for groups with no two same dates). for groups with no two same dates).
subevent_date_from datetime The first date time of a subevent to which this discount can be applied
(or ``null``). Ignored in non-series events.
subevent_date_until datetime The last date time of a subevent to which this discount can be applied
(or ``null``). Ignored in non-series events.
condition_all_products boolean If ``true``, the discount condition applies to all items. condition_all_products boolean If ``true``, the discount condition applies to all items.
condition_limit_products list of integers If ``condition_all_products`` is not set, this is a list condition_limit_products list of integers If ``condition_all_products`` is not set, this is a list
of internal item IDs that the discount condition applies to. of internal item IDs that the discount condition applies to.
@@ -105,6 +109,8 @@ Endpoints
"available_from": null, "available_from": null,
"available_until": null, "available_until": null,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": null,
"subevent_date_until": null,
"condition_all_products": true, "condition_all_products": true,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": true, "condition_apply_to_addons": true,
@@ -163,6 +169,8 @@ Endpoints
"available_from": null, "available_from": null,
"available_until": null, "available_until": null,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": null,
"subevent_date_until": null,
"condition_all_products": true, "condition_all_products": true,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": true, "condition_apply_to_addons": true,
@@ -207,6 +215,8 @@ Endpoints
"available_from": null, "available_from": null,
"available_until": null, "available_until": null,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": null,
"subevent_date_until": null,
"condition_all_products": true, "condition_all_products": true,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": true, "condition_apply_to_addons": true,
@@ -240,6 +250,8 @@ Endpoints
"available_from": null, "available_from": null,
"available_until": null, "available_until": null,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": null,
"subevent_date_until": null,
"condition_all_products": true, "condition_all_products": true,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": true, "condition_apply_to_addons": true,
@@ -302,6 +314,8 @@ Endpoints
"available_from": null, "available_from": null,
"available_until": null, "available_until": null,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": null,
"subevent_date_until": null,
"condition_all_products": true, "condition_all_products": true,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": true, "condition_apply_to_addons": true,

View File

@@ -38,11 +38,12 @@ class DiscountSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
class Meta: class Meta:
model = Discount model = Discount
fields = ('id', 'active', 'internal_name', 'position', 'all_sales_channels', 'limit_sales_channels', fields = ('id', 'active', 'internal_name', 'position', 'all_sales_channels', 'limit_sales_channels',
'available_from', 'available_until', 'subevent_mode', 'condition_all_products', 'available_from', 'available_until', 'subevent_mode', 'subevent_date_from', 'subevent_date_until',
'condition_limit_products', 'condition_apply_to_addons', 'condition_min_count', 'condition_min_value', 'condition_all_products', 'condition_limit_products', 'condition_apply_to_addons',
'benefit_discount_matching_percent', 'benefit_only_apply_to_cheapest_n_matches', 'condition_min_count', 'condition_min_value', 'benefit_discount_matching_percent',
'benefit_same_products', 'benefit_limit_products', 'benefit_apply_to_addons', 'benefit_only_apply_to_cheapest_n_matches', 'benefit_same_products', 'benefit_limit_products',
'benefit_ignore_voucher_discounted', 'condition_ignore_voucher_discounted') 'benefit_apply_to_addons', 'benefit_ignore_voucher_discounted',
'condition_ignore_voucher_discounted')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@@ -1518,7 +1518,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
self.context['event'], self.context['event'],
order.sales_channel, order.sales_channel,
[ [
(cp.item_id, cp.subevent_id, cp.price, bool(cp.addon_to), cp.is_bundled, pos._voucher_discount) (cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.price,
bool(cp.addon_to), cp.is_bundled, pos._voucher_discount)
for cp in order_positions for cp in order_positions
] ]
) )

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.2.19 on 2025-03-18 09:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0278_login_source_add_unique_together'),
]
operations = [
migrations.AddField(
model_name='discount',
name='subevent_date_from',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='discount',
name='subevent_date_until',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -36,7 +36,9 @@ from django_scopes import ScopedManager
from pretix.base.decimal import round_decimal from pretix.base.decimal import round_decimal
from pretix.base.models.base import LoggedModel from pretix.base.models.base import LoggedModel
PositionInfo = namedtuple('PositionInfo', ['item_id', 'subevent_id', 'line_price_gross', 'is_addon_to', 'voucher_discount']) PositionInfo = namedtuple('PositionInfo',
['item_id', 'subevent_id', 'subevent_date_from', 'line_price_gross', 'is_addon_to',
'voucher_discount'])
class Discount(LoggedModel): class Discount(LoggedModel):
@@ -171,6 +173,17 @@ class Discount(LoggedModel):
"access to sold-out quota will still receive the discount."), "access to sold-out quota will still receive the discount."),
) )
subevent_date_from = models.DateTimeField(
verbose_name=pgettext_lazy("subevent", "Available for dates starting from"),
null=True,
blank=True,
)
subevent_date_until = models.DateTimeField(
verbose_name=pgettext_lazy("subevent", "Available for dates starting until"),
null=True,
blank=True,
)
# more feature ideas: # more feature ideas:
# - max_usages_per_order # - max_usages_per_order
# - promote_to_user_if_almost_satisfied # - promote_to_user_if_almost_satisfied
@@ -355,11 +368,15 @@ class Discount(LoggedModel):
# First, filter out everything not even covered by our product scope # First, filter out everything not even covered by our product scope
condition_candidates = [ condition_candidates = [
idx idx
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items() for idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount) in
positions.items()
if ( if (
(self.condition_all_products or item_id in limit_products) and (self.condition_all_products or item_id in limit_products) and
(self.condition_apply_to_addons or not is_addon_to) and (self.condition_apply_to_addons or not is_addon_to) and
(not self.condition_ignore_voucher_discounted or voucher_discount is None or voucher_discount == Decimal('0.00')) (not self.condition_ignore_voucher_discounted or voucher_discount is None or voucher_discount == Decimal('0.00'))
and (not subevent_id or (
self.subevent_date_from is None or subevent_date_from >= self.subevent_date_from)) and (
self.subevent_date_until is None or subevent_date_from <= self.subevent_date_until)
) )
] ]
@@ -369,7 +386,8 @@ class Discount(LoggedModel):
benefit_products = {p.pk for p in self.benefit_limit_products.all()} benefit_products = {p.pk for p in self.benefit_limit_products.all()}
benefit_candidates = [ benefit_candidates = [
idx idx
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items() for idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount) in
positions.items()
if ( if (
item_id in benefit_products and item_id in benefit_products and
(self.benefit_apply_to_addons or not is_addon_to) and (self.benefit_apply_to_addons or not is_addon_to) and

View File

@@ -1398,7 +1398,8 @@ class CartManager:
self.event, self.event,
self._sales_channel.identifier, self._sales_channel.identifier,
[ [
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher) (cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
for cp in positions for cp in positions
] ]
) )

View File

@@ -120,7 +120,8 @@ class CrossSellingService:
self.event, self.event,
self.sales_channel, self.sales_channel,
[ [
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, (cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
bool(cp.addon_to), cp.is_bundled,
cp.listed_price - cp.price_after_voucher) cp.listed_price - cp.price_after_voucher)
for cp in self.cartpositions for cp in self.cartpositions
], ],

View File

@@ -875,7 +875,8 @@ def _check_positions(event: Event, now_dt: datetime, time_machine_now_dt: dateti
event, event,
sales_channel.identifier, sales_channel.identifier,
[ [
(cp.item_id, cp.subevent_id, cp.line_price_gross, bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher) (cp.item_id, cp.subevent_id, cp.subevent.date_from if cp.subevent_id else None, cp.line_price_gross,
bool(cp.addon_to), cp.is_bundled, cp.listed_price - cp.price_after_voucher)
for cp in sorted_positions for cp in sorted_positions
] ]
) )

View File

@@ -21,6 +21,7 @@
# #
import re import re
from collections import defaultdict from collections import defaultdict
from datetime import datetime
from decimal import Decimal from decimal import Decimal
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
@@ -162,14 +163,14 @@ def get_line_price(price_after_voucher: Decimal, custom_price_input: Decimal, cu
def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel], def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
positions: List[Tuple[int, Optional[int], Decimal, bool, bool, Decimal]], positions: List[Tuple[int, Optional[int], Optional[datetime], Decimal, bool, bool, Decimal]],
collect_potential_discounts: Optional[defaultdict]=None) -> List[Tuple[Decimal, Optional[Discount]]]: collect_potential_discounts: Optional[defaultdict]=None) -> List[Tuple[Decimal, Optional[Discount]]]:
""" """
Applies any dynamic discounts to a cart Applies any dynamic discounts to a cart
:param event: Event the cart belongs to :param event: Event the cart belongs to
:param sales_channel: Sales channel the cart was created with :param sales_channel: Sales channel the cart was created with
:param positions: Tuple of the form ``(item_id, subevent_id, line_price_gross, is_addon_to, is_bundled, voucher_discount)`` :param positions: Tuple of the form ``(item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)``
:param collect_potential_discounts: If a `defaultdict(list)` is supplied, all discounts that could be applied to the cart :param collect_potential_discounts: If a `defaultdict(list)` is supplied, all discounts that could be applied to the cart
based on the "consumed" items, but lack matching "benefitting" items will be collected therein. based on the "consumed" items, but lack matching "benefitting" items will be collected therein.
The dict will contain a mapping from index in the `positions` list of the item that could be consumed, to a list The dict will contain a mapping from index in the `positions` list of the item that could be consumed, to a list
@@ -191,12 +192,14 @@ def apply_discounts(event: Event, sales_channel: Union[str, SalesChannel],
).prefetch_related('condition_limit_products', 'benefit_limit_products').order_by('position', 'pk') ).prefetch_related('condition_limit_products', 'benefit_limit_products').order_by('position', 'pk')
for discount in discount_qs: for discount in discount_qs:
result = discount.apply({ result = discount.apply({
idx: PositionInfo(item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) idx: PositionInfo(item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, voucher_discount)
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, is_bundled, voucher_discount) in enumerate(positions) for
idx, (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)
in enumerate(positions)
if not is_bundled and idx not in new_prices if not is_bundled and idx not in new_prices
}, collect_potential_discounts) }, collect_potential_discounts)
for k in result.keys(): for k in result.keys():
result[k] = (result[k], discount) result[k] = (result[k], discount)
new_prices.update(result) new_prices.update(result)
return [new_prices.get(idx, (p[2], None)) for idx, p in enumerate(positions)] return [new_prices.get(idx, (p[3], None)) for idx, p in enumerate(positions)]

View File

@@ -45,6 +45,8 @@ class DiscountForm(I18nModelForm):
'limit_sales_channels', 'limit_sales_channels',
'available_from', 'available_from',
'available_until', 'available_until',
'subevent_date_from',
'subevent_date_until',
'subevent_mode', 'subevent_mode',
'condition_all_products', 'condition_all_products',
'condition_limit_products', 'condition_limit_products',
@@ -62,6 +64,8 @@ class DiscountForm(I18nModelForm):
field_classes = { field_classes = {
'available_from': SplitDateTimeField, 'available_from': SplitDateTimeField,
'available_until': SplitDateTimeField, 'available_until': SplitDateTimeField,
'subevent_date_from': SplitDateTimeField,
'subevent_date_until': SplitDateTimeField,
'condition_limit_products': ItemMultipleChoiceField, 'condition_limit_products': ItemMultipleChoiceField,
'benefit_limit_products': ItemMultipleChoiceField, 'benefit_limit_products': ItemMultipleChoiceField,
'limit_sales_channels': SafeModelMultipleChoiceField, 'limit_sales_channels': SafeModelMultipleChoiceField,
@@ -70,6 +74,8 @@ class DiscountForm(I18nModelForm):
'subevent_mode': forms.RadioSelect, 'subevent_mode': forms.RadioSelect,
'available_from': SplitDateTimePickerWidget(), 'available_from': SplitDateTimePickerWidget(),
'available_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_available_from_0'}), 'available_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_available_from_0'}),
'subevent_date_from': SplitDateTimePickerWidget(),
'subevent_date_until': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_subevent_date_from_0'}),
'condition_limit_products': forms.CheckboxSelectMultiple(attrs={ 'condition_limit_products': forms.CheckboxSelectMultiple(attrs={
'data-inverse-dependency': '<[name$=all_products]', 'data-inverse-dependency': '<[name$=all_products]',
'class': 'scrolling-multiple-choice', 'class': 'scrolling-multiple-choice',

View File

@@ -26,6 +26,8 @@
{% bootstrap_field form.condition_ignore_voucher_discounted layout="control" %} {% bootstrap_field form.condition_ignore_voucher_discounted layout="control" %}
{% if form.subevent_mode %} {% if form.subevent_mode %}
{% bootstrap_field form.subevent_mode layout="control" %} {% bootstrap_field form.subevent_mode layout="control" %}
{% bootstrap_field form.subevent_date_from layout="control" %}
{% bootstrap_field form.subevent_date_until layout="control" %}
{% endif %} {% endif %}
<div class="form-group form-alternatives"> <div class="form-group form-alternatives">
<label class="col-md-3 control-label"> <label class="col-md-3 control-label">

View File

@@ -47,6 +47,8 @@ TEST_DISCOUNT_RES = {
"available_from": None, "available_from": None,
"available_until": None, "available_until": None,
"subevent_mode": "mixed", "subevent_mode": "mixed",
"subevent_date_from": None,
"subevent_date_until": None,
"condition_all_products": True, "condition_all_products": True,
"condition_limit_products": [], "condition_limit_products": [],
"condition_apply_to_addons": True, "condition_apply_to_addons": True,

View File

@@ -142,7 +142,7 @@ testcases_single_rule = [
( (
mixed_min_count_matching_percent, mixed_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 2, ) * 2,
( (
Decimal('120.00'), Decimal('120.00'),
@@ -152,7 +152,7 @@ testcases_single_rule = [
( (
mixed_min_count_matching_percent, mixed_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 3, ) * 3,
( (
Decimal('96.00'), Decimal('96.00'),
@@ -163,12 +163,12 @@ testcases_single_rule = [
( (
mixed_min_count_matching_percent, mixed_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -184,7 +184,7 @@ testcases_single_rule = [
( (
mixed_min_count_one_free, mixed_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 2, ) * 2,
( (
Decimal('120.00'), Decimal('120.00'),
@@ -194,7 +194,7 @@ testcases_single_rule = [
( (
mixed_min_count_one_free, mixed_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 3, ) * 3,
( (
Decimal('0.00'), Decimal('0.00'),
@@ -205,7 +205,7 @@ testcases_single_rule = [
( (
mixed_min_count_one_free, mixed_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 5, ) * 5,
( (
Decimal('0.00'), Decimal('0.00'),
@@ -218,7 +218,7 @@ testcases_single_rule = [
( (
mixed_min_count_one_free, mixed_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 6, ) * 6,
( (
Decimal('0.00'), Decimal('0.00'),
@@ -232,12 +232,12 @@ testcases_single_rule = [
( (
mixed_min_count_one_free, mixed_min_count_one_free,
( (
(1, 1, Decimal('1.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('2.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('3.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('3.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('4.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('5.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('5.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('6.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('6.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -253,7 +253,7 @@ testcases_single_rule = [
( (
mixed_min_value_matching_percent, mixed_min_value_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 4, ) * 4,
( (
Decimal('120.00'), Decimal('120.00'),
@@ -265,7 +265,7 @@ testcases_single_rule = [
( (
mixed_min_value_matching_percent, mixed_min_value_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 5, ) * 5,
( (
Decimal('96.00'), Decimal('96.00'),
@@ -278,7 +278,7 @@ testcases_single_rule = [
( (
mixed_min_value_matching_percent, mixed_min_value_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
) * 10, ) * 10,
( (
Decimal('96.00'), Decimal('96.00'),
@@ -298,10 +298,10 @@ testcases_single_rule = [
( (
same_min_count_matching_percent, same_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -313,11 +313,11 @@ testcases_single_rule = [
( (
same_min_count_matching_percent, same_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -330,14 +330,14 @@ testcases_single_rule = [
( (
same_min_count_matching_percent, same_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -355,15 +355,15 @@ testcases_single_rule = [
( (
same_min_count_one_free, same_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -380,15 +380,15 @@ testcases_single_rule = [
( (
same_min_count_one_free, same_min_count_one_free,
( (
(1, 1, Decimal('1.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('2.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('3.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('3.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('4.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('5.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('5.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('6.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('6.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('7.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('7.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('8.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('8.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('9.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('9.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -407,15 +407,15 @@ testcases_single_rule = [
( (
same_min_value_matching_percent, same_min_value_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -434,9 +434,9 @@ testcases_single_rule = [
( (
distinct_min_count_matching_percent, distinct_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -447,9 +447,9 @@ testcases_single_rule = [
( (
distinct_min_count_matching_percent, distinct_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -460,14 +460,14 @@ testcases_single_rule = [
( (
distinct_min_count_matching_percent, distinct_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -483,13 +483,13 @@ testcases_single_rule = [
( (
distinct_min_count_matching_percent, distinct_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('120.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -504,13 +504,13 @@ testcases_single_rule = [
( (
distinct_min_count_matching_percent, distinct_min_count_matching_percent,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('120.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('96.00'), Decimal('96.00'),
@@ -527,9 +527,9 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -540,9 +540,9 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -553,12 +553,12 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('120.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -572,12 +572,12 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('3.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('3.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('2.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('1.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('1.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('2.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('3.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('3.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('3.00'), Decimal('3.00'),
@@ -591,12 +591,12 @@ testcases_single_rule = [
( (
distinct_min_count_two_free, distinct_min_count_two_free,
( (
(1, 1, Decimal('3.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('3.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('2.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('1.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('1.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('2.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('3.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('3.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('3.00'), Decimal('3.00'),
@@ -610,12 +610,12 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('120.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('120.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('120.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('120.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 5, Decimal('120.00'), False, False, Decimal('0.00')), (1, 5, now(), Decimal('120.00'), False, False, Decimal('0.00')),
(1, 6, Decimal('120.00'), False, False, Decimal('0.00')), (1, 6, now(), Decimal('120.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('120.00'), Decimal('120.00'),
@@ -629,12 +629,12 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('1.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('1.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('2.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('2.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('3.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('3.00'), False, False, Decimal('0.00')),
(1, 4, Decimal('4.00'), False, False, Decimal('0.00')), (1, 4, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 5, Decimal('5.00'), False, False, Decimal('0.00')), (1, 5, now(), Decimal('5.00'), False, False, Decimal('0.00')),
(1, 6, Decimal('6.00'), False, False, Decimal('0.00')), (1, 6, now(), Decimal('6.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -648,12 +648,12 @@ testcases_single_rule = [
( (
distinct_min_count_one_free, distinct_min_count_one_free,
( (
(1, 1, Decimal('4.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('4.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('4.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('4.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('6.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('6.00'), False, False, Decimal('0.00')),
(1, 2, Decimal('6.00'), False, False, Decimal('0.00')), (1, 2, now(), Decimal('6.00'), False, False, Decimal('0.00')),
(1, 3, Decimal('6.00'), False, False, Decimal('0.00')), (1, 3, now(), Decimal('6.00'), False, False, Decimal('0.00')),
), ),
( (
# This one is unexpected, since the customer could get a lower price # This one is unexpected, since the customer could get a lower price
@@ -674,7 +674,7 @@ testcases_single_rule = [
Discount(condition_min_count=1, benefit_discount_matching_percent=20), Discount(condition_min_count=1, benefit_discount_matching_percent=20),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -689,8 +689,8 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('0.00'), Decimal('0.00'),
@@ -708,9 +708,9 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('100.00'), Decimal('100.00'),
@@ -728,9 +728,9 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -747,11 +747,11 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -770,9 +770,9 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, False, Decimal('0.00')),
), ),
( (
Decimal('100.00'), Decimal('100.00'),
@@ -791,9 +791,9 @@ testcases_single_rule = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, True, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, True, Decimal('0.00')),
(1, 1, Decimal('100.00'), True, True, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), True, True, Decimal('0.00')),
), ),
( (
Decimal('100.00'), Decimal('100.00'),
@@ -818,9 +818,9 @@ testcases_multiple_rules = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -843,9 +843,9 @@ testcases_multiple_rules = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -867,9 +867,9 @@ testcases_multiple_rules = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('100.00'), Decimal('100.00'),
@@ -890,11 +890,11 @@ testcases_multiple_rules = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('100.00'), Decimal('100.00'),
@@ -917,9 +917,9 @@ testcases_multiple_rules = [
), ),
), ),
( (
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(1, 1, Decimal('100.00'), False, False, Decimal('0.00')), (1, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
), ),
( (
Decimal('80.00'), Decimal('80.00'),
@@ -954,10 +954,10 @@ def test_limit_products(event, item, item2):
d2.save() d2.save()
positions = ( positions = (
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('80.00'), Decimal('80.00'),
@@ -979,10 +979,10 @@ def test_limit_products_subevents_distinct(event, item, item2):
d1.condition_limit_products.add(item) d1.condition_limit_products.add(item)
positions = ( positions = (
(item.pk, 1, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, 1, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, 2, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, 2, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, 3, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, 3, now(), Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, 4, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, 4, now(), Decimal('50.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('80.00'), Decimal('80.00'),
@@ -1007,8 +1007,8 @@ def test_sales_channels(event, item):
d2.limit_sales_channels.add(event.organizer.sales_channels.get(identifier="bar")) d2.limit_sales_channels.add(event.organizer.sales_channels.get(identifier="bar"))
positions = ( positions = (
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
) )
assert sorted([p for p, d in apply_discounts(event, 'bar', positions)]) == [Decimal('80.00'), Decimal('80.00')] assert sorted([p for p, d in apply_discounts(event, 'bar', positions)]) == [Decimal('80.00'), Decimal('80.00')]
@@ -1024,8 +1024,8 @@ def test_available_from(event, item):
d2.save() d2.save()
positions = ( positions = (
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
) )
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')] assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')]
@@ -1040,13 +1040,78 @@ def test_available_until(event, item):
d2.save() d2.save()
positions = ( positions = (
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
) )
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')] assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')]
@pytest.mark.django_db
@scopes_disabled()
def test_subevent_date_from(event, item, subevent):
subevent_date = subevent.date_from # prevent test timing errors
d1 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=20,
subevent_date_from=subevent_date + timedelta(days=1))
d1.save()
d2 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=50,
subevent_date_from=subevent_date)
d2.save()
# (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)
positions = (
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
)
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')]
@pytest.mark.django_db
@scopes_disabled()
def test_subevent_date_until(event, item, subevent):
subevent_date = subevent.date_from # prevent test timing errors
d1 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=20,
subevent_date_until=subevent_date + timedelta(days=1))
d1.save()
d2 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=50,
subevent_date_until=subevent_date)
d2.save()
# (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)
positions = (
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
)
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('80.00'), Decimal('80.00')]
@pytest.mark.django_db
@scopes_disabled()
def test_subevent_date_from_until(event, item, subevent):
subevent_date = subevent.date_from # prevent test timing errors
d1 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=20,
subevent_date_from=subevent_date + timedelta(days=1),
subevent_date_until=subevent_date + timedelta(days=2))
d1.save()
d2 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=50,
subevent_date_from=subevent_date - timedelta(days=2),
subevent_date_until=subevent_date - timedelta(days=1))
d2.save()
d3 = Discount(event=event, condition_min_count=2, benefit_discount_matching_percent=80,
subevent_date_from=subevent_date, subevent_date_until=subevent_date + timedelta(days=1))
d3.save()
# (item_id, subevent_id, subevent_date_from, line_price_gross, is_addon_to, is_bundled, voucher_discount)
positions = (
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, subevent.pk, subevent.date_from, Decimal('100.00'), False, False, Decimal('0.00')),
)
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('20.00'), Decimal('20.00')]
@pytest.mark.django_db @pytest.mark.django_db
@scopes_disabled() @scopes_disabled()
def test_discount_other_products_min_count(event, item, item2): def test_discount_other_products_min_count(event, item, item2):
@@ -1064,21 +1129,21 @@ def test_discount_other_products_min_count(event, item, item2):
d1.benefit_limit_products.add(item) d1.benefit_limit_products.add(item)
positions = ( positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('100.00'), Decimal('100.00'),
@@ -1120,12 +1185,12 @@ def test_discount_other_products_min_count_no_addon(event, item, item2):
d1.benefit_limit_products.add(item) d1.benefit_limit_products.add(item)
positions = ( positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), True, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), True, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('100.00'), Decimal('100.00'),
@@ -1158,13 +1223,13 @@ def test_discount_other_products_min_count_no_voucher(event, item, item2):
d1.benefit_limit_products.add(item) d1.benefit_limit_products.add(item)
positions = ( positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('40.00'), False, False, Decimal('50.00')), (item.pk, None, None, Decimal('40.00'), False, False, Decimal('50.00')),
(item.pk, None, Decimal('40.00'), False, False, Decimal('50.00')), (item.pk, None, None, Decimal('40.00'), False, False, Decimal('50.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('100.00'), Decimal('100.00'),
@@ -1199,21 +1264,21 @@ def test_discount_subgroup_cheapest_n_min_count(event, item, item2):
positions = ( positions = (
# 11 items of item2, which contribute to the total count of 15, but do not get reduced # 11 items of item2, which contribute to the total count of 15, but do not get reduced
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('100.00'), False, False, Decimal('0.00')),
# 4 items of item, of which 3 of the cheapest will be reduced # 4 items of item, of which 3 of the cheapest will be reduced
(item.pk, None, Decimal('110.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('110.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('110.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('110.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('90.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('100.00'), Decimal('100.00'),
@@ -1252,10 +1317,10 @@ def test_discount_other_products_min_value(event, item, item2):
d1.benefit_limit_products.add(item2) d1.benefit_limit_products.add(item2)
positions = ( positions = (
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('50.00'), Decimal('50.00'),
@@ -1268,11 +1333,11 @@ def test_discount_other_products_min_value(event, item, item2):
assert sorted(new_prices) == sorted(expected) assert sorted(new_prices) == sorted(expected)
positions = ( positions = (
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
Decimal('50.00'), Decimal('50.00'),
@@ -1316,16 +1381,16 @@ def test_multiple_discounts_with_benefit_condition_overlap(event, item, item2):
d2.condition_limit_products.add(item) d2.condition_limit_products.add(item)
positions = ( positions = (
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
# item2 remains untouched # item2 remains untouched
@@ -1379,16 +1444,16 @@ def test_multiple_discounts_with_same_condition(event, item, item2, item3):
d2.benefit_limit_products.add(item3) d2.benefit_limit_products.add(item3)
positions = ( positions = (
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item3.pk, None, Decimal('42.00'), False, False, Decimal('0.00')), (item3.pk, None, None, Decimal('42.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')), (item2.pk, None, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')), (item.pk, None, None, Decimal('23.00'), False, False, Decimal('0.00')),
) )
expected = ( expected = (
# both item1 remain full price # both item1 remain full price