forked from CGM_Public/pretix_original
Added products that require a voucher to be bought/shown
This commit is contained in:
25
src/pretix/base/migrations/0024_auto_20160728_1725.py
Normal file
25
src/pretix/base/migrations/0024_auto_20160728_1725.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2016-07-28 17:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0023_auto_20160601_1039'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='hide_without_voucher',
|
||||
field=models.BooleanField(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).', verbose_name='This product will only be shown if a voucher is redeemed.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='require_voucher',
|
||||
field=models.BooleanField(default=False, help_text='To buy this product, the user needs a voucher that applies to this product either directly or via a quota.', verbose_name='This product can only be bought using a voucher.'),
|
||||
),
|
||||
]
|
||||
@@ -173,6 +173,18 @@ class Item(LoggedModel):
|
||||
null=True, blank=True,
|
||||
help_text=_('This product will not be sold after the given date.')
|
||||
)
|
||||
require_voucher = models.BooleanField(
|
||||
verbose_name=_('This product can only be bought using a voucher.'),
|
||||
default=False,
|
||||
help_text=_('To buy this product, the user needs a voucher that applies to this product '
|
||||
'either directly or via a quota.')
|
||||
)
|
||||
hide_without_voucher = models.BooleanField(
|
||||
verbose_name=_('This product will only be shown if a voucher 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).')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Product")
|
||||
|
||||
@@ -32,7 +32,8 @@ error_messages = {
|
||||
'voucher_invalid': _('This voucher code is not known in our database.'),
|
||||
'voucher_redeemed': _('This voucher code has already been used an can only be used once.'),
|
||||
'voucher_expired': _('This voucher is expired.'),
|
||||
'voucher_invalid_item': _('This voucher is not valid for this item.'),
|
||||
'voucher_invalid_item': _('This voucher is not valid for this product.'),
|
||||
'voucher_required': _('You need a valid voucher code to order this product.'),
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +131,12 @@ def _add_new_items(event: Event, items: List[dict],
|
||||
if voucher and voucher.quota and voucher.quota.pk not in [q.pk for q in quotas]:
|
||||
return error_messages['voucher_invalid_item']
|
||||
|
||||
if item.require_voucher and voucher is None:
|
||||
return error_messages['voucher_required']
|
||||
|
||||
if item.hide_without_voucher and (voucher is None or voucher.item is None or voucher.item.pk != item.pk):
|
||||
return error_messages['voucher_required']
|
||||
|
||||
if len(quotas) == 0 or not item.is_available():
|
||||
err = err or error_messages['unavailable']
|
||||
continue
|
||||
|
||||
@@ -142,6 +142,8 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'tax_rate',
|
||||
'available_from',
|
||||
'available_until',
|
||||
'require_voucher',
|
||||
'hide_without_voucher',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
<legend>{% trans "Availability" %}</legend>
|
||||
{% bootstrap_field form.available_from layout="horizontal" %}
|
||||
{% bootstrap_field form.available_until layout="horizontal" %}
|
||||
{% bootstrap_field form.require_voucher layout="horizontal" %}
|
||||
{% bootstrap_field form.hide_without_voucher layout="horizontal" %}
|
||||
</fieldset>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -174,7 +174,13 @@
|
||||
{% endblocktrans %}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if item.cached_availability.0 == 100 %}
|
||||
{% if item.require_voucher %}
|
||||
<div class="col-md-2 col-xs-6 availability-box unavailable">
|
||||
<small>
|
||||
{% trans "Enter a voucher code below to buy this ticket." %}
|
||||
</small>
|
||||
</div>
|
||||
{% elif item.cached_availability.0 == 100 %}
|
||||
<div class="col-md-2 col-xs-6 availability-box available">
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ item.order_max }}" name="item_{{ item.id }}">
|
||||
|
||||
@@ -154,12 +154,15 @@ class RedeemView(EventViewMixin, TemplateView):
|
||||
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
|
||||
)
|
||||
|
||||
vouchq = Q(hide_without_voucher=False)
|
||||
|
||||
if self.voucher.item_id:
|
||||
vouchq |= Q(pk=self.voucher.item_id)
|
||||
items = items.filter(pk=self.voucher.item_id)
|
||||
elif self.voucher.quota_id:
|
||||
items = items.filter(quotas__in=[self.voucher.quota_id])
|
||||
|
||||
items = items.select_related(
|
||||
items = items.filter(vouchq).select_related(
|
||||
'category', # for re-grouping
|
||||
).prefetch_related(
|
||||
'quotas', 'variations__quotas', 'quotas__event' # for .availability()
|
||||
|
||||
@@ -31,6 +31,7 @@ class EventIndex(EventViewMixin, CartMixin, TemplateView):
|
||||
Q(active=True)
|
||||
& Q(Q(available_from__isnull=True) | Q(available_from__lte=now()))
|
||||
& Q(Q(available_until__isnull=True) | Q(available_until__gte=now()))
|
||||
& Q(hide_without_voucher=False)
|
||||
).select_related(
|
||||
'category', # for re-grouping
|
||||
).prefetch_related(
|
||||
|
||||
@@ -578,3 +578,45 @@ class CartTest(CartTestMixin, TestCase):
|
||||
doc = BeautifulSoup(response.rendered_content)
|
||||
self.assertIn('already been used', doc.select('.alert-danger')[0].text)
|
||||
self.assertEqual(1, CartPosition.objects.filter(cart_id=self.session_key, event=self.event).count())
|
||||
|
||||
def test_require_voucher(self):
|
||||
v = Voucher.objects.create(quota=self.quota_shirts, event=self.event)
|
||||
self.shirt.require_voucher = True
|
||||
self.shirt.save()
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'variation_%d_%d_voucher' % (self.shirt.id, self.shirt_red.id): v.code,
|
||||
}, follow=True)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 1)
|
||||
self.assertEqual(objs[0].item, self.shirt)
|
||||
self.assertEqual(objs[0].variation, self.shirt_red)
|
||||
|
||||
def test_require_voucher_failed(self):
|
||||
self.shirt.require_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',
|
||||
}, follow=True)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 0)
|
||||
|
||||
def test_hide_without_voucher(self):
|
||||
v = Voucher.objects.create(item=self.shirt, event=self.event)
|
||||
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_voucher' % (self.shirt.id, self.shirt_red.id): v.code,
|
||||
}, follow=True)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 1)
|
||||
self.assertEqual(objs[0].item, self.shirt)
|
||||
self.assertEqual(objs[0].variation, self.shirt_red)
|
||||
|
||||
def test_hide_without_voucher_failed(self):
|
||||
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',
|
||||
}, follow=True)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 0)
|
||||
|
||||
@@ -96,6 +96,14 @@ class ItemDisplayTest(EventTestMixin, BrowserTest):
|
||||
self.driver.get('%s/%s/%s/' % (self.live_server_url, self.orga.slug, self.event.slug))
|
||||
self.assertNotIn("Early-bird", self.driver.find_element_by_css_selector("body").text)
|
||||
|
||||
def test_hidden_without_voucher(self):
|
||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True,
|
||||
hide_without_voucher=True)
|
||||
q.items.add(item)
|
||||
self.driver.get('%s/%s/%s/' % (self.live_server_url, self.orga.slug, self.event.slug))
|
||||
self.assertNotIn("Early-bird", self.driver.find_element_by_css_selector("body").text)
|
||||
|
||||
def test_simple_with_category(self):
|
||||
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||
|
||||
Reference in New Issue
Block a user