Backwards-compatible implementation

This commit is contained in:
Raphael Michel
2019-07-07 12:20:41 +02:00
parent 435d90474a
commit 481db3cb88
12 changed files with 52 additions and 6 deletions

View File

@@ -41,6 +41,7 @@ quota integer An ID of a quot
tag string A string that is used for grouping vouchers tag string A string that is used for grouping vouchers
comment string An internal comment on the voucher comment string An internal comment on the voucher
subevent integer ID of the date inside an event series this voucher belongs to (or ``null``). subevent integer ID of the date inside an event series this voucher belongs to (or ``null``).
show_hidden_items boolean Only if set to ``true``, this voucher allows to buy products with the property ``hide_without_voucher``. Defaults to ``true``.
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
@@ -48,6 +49,10 @@ subevent integer ID of the date
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added. The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
.. versionchanged:: 3.0
The attribute ``show_hidden_items`` has been added.
Endpoints Endpoints
--------- ---------

View File

@@ -27,7 +27,7 @@ class VoucherSerializer(I18nAwareModelSerializer):
model = Voucher model = Voucher
fields = ('id', 'code', 'max_usages', 'redeemed', 'valid_until', 'block_quota', fields = ('id', 'code', 'max_usages', 'redeemed', 'valid_until', 'block_quota',
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota', 'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent') 'tag', 'comment', 'subevent', 'show_hidden_items')
read_only_fields = ('id', 'redeemed') read_only_fields = ('id', 'redeemed')
list_serializer_class = VoucherListSerializer list_serializer_class = VoucherListSerializer

View File

@@ -0,0 +1,26 @@
# Generated by Django 2.2.1 on 2019-07-07 10:10
from django.db import migrations, models
def set_show_hidden_items(apps, schema_editor):
Voucher = apps.get_model('pretixbase', 'Voucher')
Voucher.objects.filter(quota__isnull=False).update(show_hidden_items=False)
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0124_seat_seat_guid'),
]
operations = [
migrations.AddField(
model_name='voucher',
name='show_hidden_items',
field=models.BooleanField(default=True),
),
migrations.RunPython(
set_show_hidden_items,
migrations.RunPython.noop,
)
]

View File

@@ -171,7 +171,7 @@ def filter_available(qs, channel='web', voucher=None, allow_addons=False):
qs = qs.filter(q) qs = qs.filter(q)
vouchq = Q(hide_without_voucher=False) vouchq = Q(hide_without_voucher=False)
if voucher: if voucher and voucher.show_hidden_items:
if voucher.item_id: if voucher.item_id:
vouchq = Q(pk=voucher.item_id) vouchq = Q(pk=voucher.item_id)
elif voucher.quota_id: elif voucher.quota_id:
@@ -342,7 +342,7 @@ class Item(LoggedModel):
verbose_name=_('This product will only be shown if a voucher matching the product is redeemed.'), verbose_name=_('This product will only be shown if a voucher matching the product is redeemed.'),
default=False, default=False,
help_text=_('This product will be hidden from the event page until the user enters a voucher ' help_text=_('This product will be hidden from the event page until the user enters a voucher '
'code that is specifically tied to this product (and not via a quota).') 'that unlocks this product.')
) )
require_bundling = models.BooleanField( require_bundling = models.BooleanField(
verbose_name=_('Only sell this product as part of a bundle'), verbose_name=_('Only sell this product as part of a bundle'),

View File

@@ -176,6 +176,10 @@ class Voucher(LoggedModel):
help_text=_("The text entered in this field will not be visible to the user and is available for your " help_text=_("The text entered in this field will not be visible to the user and is available for your "
"convenience.") "convenience.")
) )
show_hidden_items = models.BooleanField(
verbose_name=_("Shows hidden products that match this voucher"),
default=True
)
objects = ScopedManager(organizer='event__organizer') objects = ScopedManager(organizer='event__organizer')

View File

@@ -225,7 +225,7 @@ class CartManager:
def _check_item_constraints(self, op): def _check_item_constraints(self, op):
if isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation): if isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation):
if (op.item.require_voucher or op.item.hide_without_voucher) and op.voucher is None: if (op.item.require_voucher or op.item.hide_without_voucher) and (op.voucher is None or not op.voucher.show_hidden_items):
raise CartError(error_messages['voucher_required']) raise CartError(error_messages['voucher_required'])
if not op.item.is_available() or (op.variation and not op.variation.active): if not op.item.is_available() or (op.variation and not op.variation.active):

View File

@@ -505,7 +505,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
err = err or error_messages['voucher_required'] err = err or error_messages['voucher_required']
break break
if cp.item.hide_without_voucher and not cp.voucher.applies_to(cp.item.pk, cp.variation.pk): if cp.item.hide_without_voucher and (cp.voucher is None or not cp.voucher.show_hidden_items or not cp.voucher.applies_to(cp.item.pk, cp.variation.pk)):
delete(cp) delete(cp)
cp.delete() cp.delete()
err = error_messages['voucher_required'] err = error_messages['voucher_required']

View File

@@ -32,7 +32,7 @@ class VoucherForm(I18nModelForm):
localized_fields = '__all__' localized_fields = '__all__'
fields = [ fields = [
'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag', 'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag',
'comment', 'max_usages', 'price_mode', 'subevent' 'comment', 'max_usages', 'price_mode', 'subevent', 'show_hidden_items'
] ]
field_classes = { field_classes = {
'valid_until': SplitDateTimeField, 'valid_until': SplitDateTimeField,

View File

@@ -72,6 +72,7 @@
{% bootstrap_field form.allow_ignore_quota layout="control" %} {% bootstrap_field form.allow_ignore_quota layout="control" %}
{% bootstrap_field form.tag layout="control" %} {% bootstrap_field form.tag layout="control" %}
{% bootstrap_field form.comment layout="control" %} {% bootstrap_field form.comment layout="control" %}
{% bootstrap_field form.show_hidden_items layout="control" %}
</fieldset> </fieldset>
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %} {% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
<div class="form-group submit-group"> <div class="form-group submit-group">

View File

@@ -74,6 +74,7 @@
{% bootstrap_field form.allow_ignore_quota layout="control" %} {% bootstrap_field form.allow_ignore_quota layout="control" %}
{% bootstrap_field form.tag layout="control" %} {% bootstrap_field form.tag layout="control" %}
{% bootstrap_field form.comment layout="control" %} {% bootstrap_field form.comment layout="control" %}
{% bootstrap_field form.show_hidden_items layout="control" %}
</fieldset> </fieldset>
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %} {% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
</div> </div>

View File

@@ -43,6 +43,7 @@ TEST_VOUCHER_RES = {
'quota': None, 'quota': None,
'tag': 'Foo', 'tag': 'Foo',
'comment': '', 'comment': '',
'show_hidden_items': True,
'subevent': None 'subevent': None
} }

View File

@@ -435,6 +435,14 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
assert "Early-bird" in html.rendered_content assert "Early-bird" in html.rendered_content
def test_hide_wo_voucher_quota(self): def test_hide_wo_voucher_quota(self):
self.item.hide_without_voucher = True
self.item.save()
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
assert "Early-bird" in html.rendered_content
def test_hide_wo_voucher_quota_dont_show(self):
self.v.show_hidden_items = False
self.v.save()
self.item.hide_without_voucher = True self.item.hide_without_voucher = True
self.item.save() self.item.save()
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code)) html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))