Compare commits

...

4 Commits

Author SHA1 Message Date
Raphael Michel
8bbe605439 Fix voucher bulk form 2019-07-07 12:42:38 +02:00
Raphael Michel
8267da66e4 Add test 2019-07-07 12:25:08 +02:00
Raphael Michel
481db3cb88 Backwards-compatible implementation 2019-07-07 12:20:41 +02:00
Raphael Michel
435d90474a Changes in checks 2019-07-06 15:21:32 +02:00
13 changed files with 69 additions and 14 deletions

View File

@@ -41,6 +41,7 @@ quota integer An ID of a quot
tag string A string that is used for grouping vouchers
comment string An internal comment on the voucher
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.
.. versionchanged:: 3.0
The attribute ``show_hidden_items`` has been added.
Endpoints
---------

View File

@@ -27,7 +27,7 @@ class VoucherSerializer(I18nAwareModelSerializer):
model = Voucher
fields = ('id', 'code', 'max_usages', 'redeemed', 'valid_until', 'block_quota',
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent')
'tag', 'comment', 'subevent', 'show_hidden_items')
read_only_fields = ('id', 'redeemed')
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,12 +171,11 @@ def filter_available(qs, channel='web', voucher=None, allow_addons=False):
qs = qs.filter(q)
vouchq = Q(hide_without_voucher=False)
if voucher:
if voucher and voucher.show_hidden_items:
if voucher.item_id:
vouchq |= Q(pk=voucher.item_id)
qs = qs.filter(pk=voucher.item_id)
vouchq = Q(pk=voucher.item_id)
elif voucher.quota_id:
qs = qs.filter(quotas__in=[voucher.quota_id])
vouchq = Q(quotas__in=[voucher.quota_id])
return qs.filter(vouchq)
@@ -343,7 +342,7 @@ class Item(LoggedModel):
verbose_name=_('This product will only be shown if a voucher matching the product is redeemed.'),
default=False,
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(
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 "
"convenience.")
)
show_hidden_items = models.BooleanField(
verbose_name=_("Shows hidden products that match this voucher"),
default=True
)
objects = ScopedManager(organizer='event__organizer')

View File

@@ -225,10 +225,7 @@ class CartManager:
def _check_item_constraints(self, op):
if isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation):
if op.item.require_voucher and op.voucher is None:
raise CartError(error_messages['voucher_required'])
if op.item.hide_without_voucher and (op.voucher is None or op.voucher.item is None or op.voucher.item.pk != op.item.pk):
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'])
if not op.item.is_available() or (op.variation and not op.variation.active):

View File

@@ -505,9 +505,9 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
err = err or error_messages['voucher_required']
break
if cp.item.hide_without_voucher and (cp.voucher is None or cp.voucher.item is None
or cp.voucher.item.pk != cp.item.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)
cp.delete()
err = error_messages['voucher_required']
break

View File

@@ -32,7 +32,7 @@ class VoucherForm(I18nModelForm):
localized_fields = '__all__'
fields = [
'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 = {
'valid_until': SplitDateTimeField,
@@ -197,7 +197,7 @@ class VoucherBulkForm(VoucherForm):
localized_fields = '__all__'
fields = [
'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag', 'comment',
'max_usages', 'price_mode', 'subevent'
'max_usages', 'price_mode', 'subevent', 'show_hidden_items'
]
field_classes = {
'valid_until': SplitDateTimeField,

View File

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

View File

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

View File

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

View File

@@ -1529,6 +1529,19 @@ class CartTest(CartTestMixin, TestCase):
self.assertEqual(objs[0].item, self.shirt)
self.assertEqual(objs[0].variation, self.shirt_red)
def test_hide_without_voucher_failed_because_of_voucher(self):
with scopes_disabled():
v = Voucher.objects.create(item=self.shirt, event=self.event, show_hidden_items=False)
self.shirt.hide_without_voucher = True
self.shirt.save()
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1',
'_voucher_code': v.code
}, follow=True)
with scopes_disabled():
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
self.assertEqual(len(objs), 0)
def test_hide_without_voucher_failed(self):
self.shirt.hide_without_voucher = True
self.shirt.save()

View File

@@ -435,6 +435,14 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
assert "Early-bird" in html.rendered_content
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.save()
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))