forked from CGM_Public/pretix_original
Compare commits
4 Commits
actions-de
...
hidden-pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bbe605439 | ||
|
|
8267da66e4 | ||
|
|
481db3cb88 | ||
|
|
435d90474a |
@@ -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
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
26
src/pretix/base/migrations/0125_voucher_show_hidden_items.py
Normal file
26
src/pretix/base/migrations/0125_voucher_show_hidden_items.py
Normal 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,
|
||||||
|
)
|
||||||
|
]
|
||||||
@@ -171,12 +171,11 @@ 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)
|
||||||
qs = qs.filter(pk=voucher.item_id)
|
|
||||||
elif voucher.quota_id:
|
elif voucher.quota_id:
|
||||||
qs = qs.filter(quotas__in=[voucher.quota_id])
|
vouchq = Q(quotas__in=[voucher.quota_id])
|
||||||
return qs.filter(vouchq)
|
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.'),
|
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'),
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
@@ -225,10 +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 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'])
|
|
||||||
|
|
||||||
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):
|
|
||||||
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):
|
||||||
|
|||||||
@@ -505,9 +505,9 @@ 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 (cp.voucher is None or cp.voucher.item is None
|
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)):
|
||||||
or cp.voucher.item.pk != cp.item.pk):
|
|
||||||
delete(cp)
|
delete(cp)
|
||||||
|
cp.delete()
|
||||||
err = error_messages['voucher_required']
|
err = error_messages['voucher_required']
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -197,7 +197,7 @@ class VoucherBulkForm(VoucherForm):
|
|||||||
localized_fields = '__all__'
|
localized_fields = '__all__'
|
||||||
fields = [
|
fields = [
|
||||||
'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag', 'comment',
|
'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 = {
|
field_classes = {
|
||||||
'valid_until': SplitDateTimeField,
|
'valid_until': SplitDateTimeField,
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1529,6 +1529,19 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.assertEqual(objs[0].item, self.shirt)
|
self.assertEqual(objs[0].item, self.shirt)
|
||||||
self.assertEqual(objs[0].variation, self.shirt_red)
|
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):
|
def test_hide_without_voucher_failed(self):
|
||||||
self.shirt.hide_without_voucher = True
|
self.shirt.hide_without_voucher = True
|
||||||
self.shirt.save()
|
self.shirt.save()
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user