Vouchers: Allow to set all addons or bundles as included (#3322)

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2023-05-22 11:59:27 +02:00
committed by GitHub
parent 5eff9a86f4
commit c75c080c5c
19 changed files with 241 additions and 23 deletions

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2.18 on 2023-05-16 11:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0239_giftcard_info'),
]
operations = [
migrations.AddField(
model_name='voucher',
name='all_addons_included',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='voucher',
name='all_bundles_included',
field=models.BooleanField(default=False),
),
]

View File

@@ -2832,8 +2832,12 @@ class CartPosition(AbstractPosition):
if self.is_bundled:
bundle = self.addon_to.item.bundles.filter(bundled_item=self.item, bundled_variation=self.variation).first()
if bundle:
listed_price = bundle.designated_price
price_after_voucher = bundle.designated_price
if self.addon_to.voucher_id and self.addon_to.voucher.all_bundles_included:
listed_price = Decimal('0.00')
price_after_voucher = Decimal('0.00')
else:
listed_price = bundle.designated_price
price_after_voucher = bundle.designated_price
if listed_price != self.listed_price or price_after_voucher != self.price_after_voucher:
self.listed_price = listed_price

View File

@@ -296,6 +296,14 @@ class Voucher(LoggedModel):
verbose_name=_("Shows hidden products that match this voucher"),
default=True
)
all_addons_included = models.BooleanField(
verbose_name=_("Offer all add-on products for free when redeeming this voucher"),
default=False
)
all_bundles_included = models.BooleanField(
verbose_name=_("Include all bundled products without a designated price when redeeming this voucher"),
default=False
)
objects = ScopedManager(organizer='event__organizer')

View File

@@ -512,7 +512,10 @@ class CartManager:
if cp.is_bundled:
bundle = cp.addon_to.item.bundles.filter(bundled_item=cp.item, bundled_variation=cp.variation).first()
if bundle:
listed_price = bundle.designated_price or Decimal('0.00')
if cp.addon_to.voucher_id and cp.addon_to.voucher.all_bundles_included:
listed_price = Decimal('0.00')
else:
listed_price = bundle.designated_price
else:
listed_price = cp.price
price_after_voucher = listed_price
@@ -712,6 +715,11 @@ class CartManager:
else:
bundle_quotas = []
if voucher and voucher.all_bundles_included:
bundled_price = Decimal('0.00')
else:
bundled_price = bundle.designated_price
bop = self.AddOperation(
count=bundle.count,
item=bitem,
@@ -722,8 +730,8 @@ class CartManager:
subevent=subevent,
bundled=[],
seat=None,
listed_price=bundle.designated_price,
price_after_voucher=bundle.designated_price,
listed_price=bundled_price,
price_after_voucher=bundled_price,
custom_price_input=None,
custom_price_input_is_net=False,
voucher_ignored=False,
@@ -809,7 +817,6 @@ class CartManager:
quota_diff = Counter() # Quota -> Number of usages
operations = []
available_categories = defaultdict(set) # CartPos -> Category IDs to choose from
price_included = defaultdict(dict) # CartPos -> CategoryID -> bool(price is included)
toplevel_cp = self.positions.filter(
addon_to__isnull=True
).prefetch_related(
@@ -819,7 +826,6 @@ class CartManager:
# Prefill some of the cache containers
for cp in toplevel_cp:
available_categories[cp.pk] = {iao.addon_category_id for iao in cp.item.addons.all()}
price_included[cp.pk] = {iao.addon_category_id: iao.price_included for iao in cp.item.addons.all()}
cpcache[cp.pk] = cp
for a in cp.addons.all():
if not a.is_bundled:

View File

@@ -1892,7 +1892,7 @@ class OrderChangeManager:
input_addons[op.id][a['item'], a['variation']] = a.get('count', 1)
selected_addons[op.id, item.category_id][a['item'], a['variation']] = a.get('count', 1)
if price_included[op.pk].get(item.category_id):
if price_included[op.pk].get(item.category_id) or (op.voucher_id and op.voucher.all_addons_included):
price = TAXED_ZERO
else:
price = get_price(

View File

@@ -110,6 +110,8 @@ def is_included_for_free(item: Item, addon_to: AbstractPosition):
return True
except ItemAddOn.DoesNotExist:
pass
if addon_to.voucher_id and addon_to.voucher.all_addons_included:
return True
return False