mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Refs #145 -- Vouchers that grant discounts
This commit is contained in:
30
src/pretix/base/migrations/0048_auto_20161129_1330.py
Normal file
30
src/pretix/base/migrations/0048_auto_20161129_1330.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.3 on 2016-11-29 13:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0047_auto_20161126_1300'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='voucher',
|
||||
name='price_mode',
|
||||
field=models.CharField(choices=[('none', 'No effect'), ('set', 'Set product price to'), ('subtract', 'Subtract from product price'), ('percent', 'Reduce product price by (%)')], default='set', max_length=100, verbose_name='Price mode'),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='voucher',
|
||||
old_name='price',
|
||||
new_name='value',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='voucher',
|
||||
name='value',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Voucher value'),
|
||||
),
|
||||
]
|
||||
@@ -1,3 +1,5 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
@@ -5,6 +7,7 @@ from django.utils.crypto import get_random_string
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from ..decimal import round_decimal
|
||||
from .base import LoggedModel
|
||||
from .event import Event
|
||||
from .items import Item, ItemVariation, Quota
|
||||
@@ -59,6 +62,13 @@ class Voucher(LoggedModel):
|
||||
* You need to either select a quota or an item
|
||||
* If you select an item that has variations but do not select a variation, you cannot set block_quota
|
||||
"""
|
||||
PRICE_MODES = (
|
||||
('none', _('No effect')),
|
||||
('set', _('Set product price to')),
|
||||
('subtract', _('Subtract from product price')),
|
||||
('percent', _('Reduce product price by (%)')),
|
||||
)
|
||||
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
@@ -98,10 +108,15 @@ class Voucher(LoggedModel):
|
||||
"If activated, a holder of this voucher code can buy tickets, even if there are none left."
|
||||
)
|
||||
)
|
||||
price = models.DecimalField(
|
||||
verbose_name=_("Set product price to"),
|
||||
price_mode = models.CharField(
|
||||
verbose_name=_("Price mode"),
|
||||
max_length=100,
|
||||
choices=PRICE_MODES,
|
||||
default='set'
|
||||
)
|
||||
value = models.DecimalField(
|
||||
verbose_name=_("Voucher value"),
|
||||
decimal_places=2, max_digits=10, null=True, blank=True,
|
||||
help_text=_('If empty, the product will cost its normal price.')
|
||||
)
|
||||
item = models.ForeignKey(
|
||||
Item, related_name='vouchers',
|
||||
@@ -208,3 +223,19 @@ class Voucher(LoggedModel):
|
||||
if self.valid_until and self.valid_until < now():
|
||||
return False
|
||||
return True
|
||||
|
||||
def calculate_price(self, original_price: Decimal) -> Decimal:
|
||||
"""
|
||||
Returns how the price given in original_price would be modified if this
|
||||
voucher is applied, i.e. replaced by a different price or reduced by a
|
||||
certain percentage. If the voucher does not modify the price, the
|
||||
original price will be returned.
|
||||
"""
|
||||
if self.value:
|
||||
if self.price_mode == 'set':
|
||||
return self.value
|
||||
elif self.price_mode == 'subtract':
|
||||
return original_price - self.value
|
||||
elif self.price_mode == 'percent':
|
||||
return round_decimal(original_price * (Decimal('100.00') - self.value) / Decimal('100.00'))
|
||||
return original_price
|
||||
|
||||
@@ -168,11 +168,10 @@ def _add_new_items(event: Event, items: List[dict],
|
||||
err = err or error_messages['in_part']
|
||||
quota_ok = min(quota_ok, avail[1])
|
||||
|
||||
if voucher and voucher.price is not None:
|
||||
price = voucher.price
|
||||
else:
|
||||
price = item.default_price if variation is None else (
|
||||
variation.default_price if variation.default_price is not None else item.default_price)
|
||||
price = item.default_price if variation is None else (
|
||||
variation.default_price if variation.default_price is not None else item.default_price)
|
||||
if voucher:
|
||||
price = voucher.calculate_price(price)
|
||||
|
||||
if item.free_price and 'price' in i and i['price'] is not None and i['price'] != "":
|
||||
custom_price = i['price']
|
||||
|
||||
@@ -233,8 +233,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
|
||||
err = err or error_messages['voucher_expired']
|
||||
cp.delete()
|
||||
continue
|
||||
if cp.voucher.price is not None:
|
||||
price = cp.voucher.price
|
||||
price = cp.voucher.calculate_price(price)
|
||||
|
||||
if price != cp.price and not (cp.item.free_price and cp.price > price):
|
||||
positions[i] = cp
|
||||
|
||||
@@ -22,8 +22,8 @@ class VoucherForm(I18nModelForm):
|
||||
model = Voucher
|
||||
localized_fields = '__all__'
|
||||
fields = [
|
||||
'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'price', 'tag',
|
||||
'comment', 'max_usages'
|
||||
'code', 'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag',
|
||||
'comment', 'max_usages', 'price_mode'
|
||||
]
|
||||
widgets = {
|
||||
'valid_until': forms.DateTimeInput(attrs={'class': 'datetimepicker'}),
|
||||
@@ -187,8 +187,8 @@ class VoucherBulkForm(VoucherForm):
|
||||
model = Voucher
|
||||
localized_fields = '__all__'
|
||||
fields = [
|
||||
'valid_until', 'block_quota', 'allow_ignore_quota', 'price', 'tag', 'comment',
|
||||
'max_usages'
|
||||
'valid_until', 'block_quota', 'allow_ignore_quota', 'value', 'tag', 'comment',
|
||||
'max_usages', 'price_mode'
|
||||
]
|
||||
widgets = {
|
||||
'valid_until': forms.DateTimeInput(attrs={'class': 'datetimepicker'}),
|
||||
|
||||
@@ -33,7 +33,15 @@
|
||||
{% bootstrap_field form.valid_until layout="horizontal" %}
|
||||
{% bootstrap_field form.block_quota layout="horizontal" %}
|
||||
{% bootstrap_field form.allow_ignore_quota layout="horizontal" %}
|
||||
{% bootstrap_field form.price layout="horizontal" %}
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label" for="id_tag">{% trans "Price effect" %}</label>
|
||||
<div class="col-md-5">
|
||||
{% bootstrap_field form.price_mode show_label=False form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.value show_label=False form_group_class="" %}
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field form.itemvar layout="horizontal" %}
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
|
||||
@@ -27,7 +27,15 @@
|
||||
{% bootstrap_field form.valid_until layout="horizontal" %}
|
||||
{% bootstrap_field form.block_quota layout="horizontal" %}
|
||||
{% bootstrap_field form.allow_ignore_quota layout="horizontal" %}
|
||||
{% bootstrap_field form.price layout="horizontal" %}
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label" for="id_tag">{% trans "Price effect" %}</label>
|
||||
<div class="col-md-5">
|
||||
{% bootstrap_field form.price_mode show_label=False form_group_class="" %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% bootstrap_field form.value show_label=False form_group_class="" %}
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field form.itemvar layout="horizontal" %}
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
|
||||
@@ -59,7 +59,7 @@ class VoucherList(EventPermissionRequiredMixin, ListView):
|
||||
|
||||
headers = [
|
||||
_('Voucher code'), _('Valid until'), _('Product'), _('Reserve quota'), _('Bypass quota'),
|
||||
_('Price'), _('Tag'), _('Redeemed'), _('Maximum usages')
|
||||
_('Price effect'), _('Value'), _('Tag'), _('Redeemed'), _('Maximum usages')
|
||||
]
|
||||
writer.writerow(headers)
|
||||
|
||||
@@ -77,7 +77,8 @@ class VoucherList(EventPermissionRequiredMixin, ListView):
|
||||
prod,
|
||||
_("Yes") if v.block_quota else _("No"),
|
||||
_("Yes") if v.allow_ignore_quota else _("No"),
|
||||
str(v.price) if v.price else "",
|
||||
v.get_price_mode_display(),
|
||||
str(v.value) if v.value else "",
|
||||
v.tag,
|
||||
str(v.redeemed),
|
||||
str(v.max_usages)
|
||||
|
||||
@@ -198,20 +198,16 @@ class RedeemView(EventViewMixin, TemplateView):
|
||||
item.cached_availability = (Quota.AVAILABILITY_OK, 1)
|
||||
else:
|
||||
item.cached_availability = item.check_quotas()
|
||||
if self.voucher.price is not None:
|
||||
item.price = self.voucher.price
|
||||
else:
|
||||
item.price = item.default_price
|
||||
item.price = self.voucher.calculate_price(item.default_price)
|
||||
else:
|
||||
for var in item.available_variations:
|
||||
if self.voucher.allow_ignore_quota or self.voucher.block_quota:
|
||||
var.cached_availability = (Quota.AVAILABILITY_OK, 1)
|
||||
else:
|
||||
var.cached_availability = list(var.check_quotas())
|
||||
if self.voucher.price is not None:
|
||||
var.price = self.voucher.price
|
||||
else:
|
||||
var.price = var.default_price if var.default_price is not None else item.default_price
|
||||
var.price = self.voucher.calculate_price(
|
||||
var.default_price if var.default_price is not None else item.default_price
|
||||
)
|
||||
|
||||
if len(item.available_variations) > 0:
|
||||
item.min_price = min([v.price for v in item.available_variations])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import datetime
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
@@ -406,6 +407,29 @@ class QuotaTestCase(BaseQuotaTestCase):
|
||||
v.clean()
|
||||
|
||||
|
||||
class VoucherTestCase(BaseQuotaTestCase):
|
||||
|
||||
def test_calculate_price_none(self):
|
||||
v = Voucher.objects.create(event=self.event, price_mode='none', value=Decimal('10.00'))
|
||||
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
|
||||
|
||||
def test_calculate_price_set_empty(self):
|
||||
v = Voucher.objects.create(event=self.event, price_mode='set')
|
||||
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
|
||||
|
||||
def test_calculate_price_set(self):
|
||||
v = Voucher.objects.create(event=self.event, price_mode='set', value=Decimal('10.00'))
|
||||
v.calculate_price(Decimal('23.42')) == Decimal('10.00')
|
||||
|
||||
def test_calculate_price_subtract(self):
|
||||
v = Voucher.objects.create(event=self.event, price_mode='subtract', value=Decimal('10.00'))
|
||||
v.calculate_price(Decimal('23.42')) == Decimal('13.42')
|
||||
|
||||
def test_calculate_price_percent(self):
|
||||
v = Voucher.objects.create(event=self.event, price_mode='percent', value=Decimal('23.00'))
|
||||
v.calculate_price(Decimal('100.00')) == Decimal('77.00')
|
||||
|
||||
|
||||
class OrderTestCase(BaseQuotaTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@@ -80,9 +80,9 @@ class VoucherFormTest(SoupTest):
|
||||
def test_csv(self):
|
||||
self.event.vouchers.create(item=self.ticket, code='ABCDEFG')
|
||||
doc = self.client.get('/control/event/%s/%s/vouchers/?download=yes' % (self.orga.slug, self.event.slug))
|
||||
assert doc.content.strip() == '"Voucher code","Valid until","Product","Reserve quota","Bypass quota","Price",' \
|
||||
'"Tag","Redeemed","Maximum usages"\r\n"ABCDEFG","","Early-bird ticket","No",' \
|
||||
'"No","","","0","1"'.encode('utf-8')
|
||||
assert doc.content.strip() == '"Voucher code","Valid until","Product","Reserve quota","Bypass quota",' \
|
||||
'"Price effect","Value","Tag","Redeemed","Maximum usages"\r\n"ABCDEFG","",' \
|
||||
'"Early-bird ticket","No","No","Set product price to","","","0","1"'.encode('utf-8')
|
||||
|
||||
def test_filter_status_valid(self):
|
||||
v = self.event.vouchers.create(item=self.ticket)
|
||||
|
||||
@@ -488,7 +488,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
event=self.event, cart_id=self.session_key, item=self.shirt, variation=self.shirt_red,
|
||||
price=14, expires=now() + timedelta(minutes=10)
|
||||
)
|
||||
v = Voucher.objects.create(item=self.shirt, variation=self.shirt_red, price=Decimal('10.00'), event=self.event)
|
||||
v = Voucher.objects.create(item=self.shirt, variation=self.shirt_red, value=Decimal('10.00'), event=self.event)
|
||||
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)
|
||||
@@ -594,7 +594,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(len(objs), 0)
|
||||
|
||||
def test_voucher_price(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event)
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': v.code,
|
||||
@@ -605,8 +605,76 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('12.00'))
|
||||
|
||||
def test_voucher_price_percent(self):
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('10.00'), price_mode='percent', event=self.event)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': 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.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('20.70'))
|
||||
|
||||
def test_voucher_price_subtract(self):
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('10.00'), price_mode='subtract', event=self.event)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': 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.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('13.00'))
|
||||
|
||||
def test_voucher_free_price(self):
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('10.00'), price_mode='percent', event=self.event)
|
||||
self.ticket.free_price = True
|
||||
self.ticket.save()
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'price_%d' % self.ticket.id: '21.00',
|
||||
'_voucher_code': v.code,
|
||||
}, follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||
self.assertIn('Early-bird', doc.select('.cart .cart-row')[0].select('strong')[0].text)
|
||||
self.assertIn('1', doc.select('.cart .cart-row')[0].select('.count')[0].text)
|
||||
self.assertIn('21', doc.select('.cart .cart-row')[0].select('.price')[0].text)
|
||||
self.assertIn('21', doc.select('.cart .cart-row')[0].select('.price')[1].text)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 1)
|
||||
self.assertEqual(objs[0].item, self.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('21.00'))
|
||||
|
||||
def test_voucher_free_price_lower_bound(self):
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('10.00'), price_mode='percent', event=self.event)
|
||||
self.ticket.free_price = False
|
||||
self.ticket.save()
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'price_%d' % self.ticket.id: '20.00',
|
||||
'_voucher_code': v.code,
|
||||
}, follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||
self.assertIn('Early-bird', doc.select('.cart .cart-row')[0].select('strong')[0].text)
|
||||
self.assertIn('1', doc.select('.cart .cart-row')[0].select('.count')[0].text)
|
||||
self.assertIn('20.70', doc.select('.cart .cart-row')[0].select('.price')[0].text)
|
||||
self.assertIn('20.70', doc.select('.cart .cart-row')[0].select('.price')[1].text)
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 1)
|
||||
self.assertEqual(objs[0].item, self.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('20.70'))
|
||||
|
||||
def test_voucher_redemed(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event, redeemed=1)
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, redeemed=1)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': v.code,
|
||||
@@ -616,7 +684,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertFalse(CartPosition.objects.filter(cart_id=self.session_key, event=self.event).exists())
|
||||
|
||||
def test_voucher_expired(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() - timedelta(days=2))
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -638,7 +706,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
def test_voucher_quota_empty(self):
|
||||
self.quota_tickets.size = 0
|
||||
self.quota_tickets.save()
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event)
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': v.code,
|
||||
@@ -650,7 +718,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
def test_voucher_quota_ignore(self):
|
||||
self.quota_tickets.size = 0
|
||||
self.quota_tickets.save()
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
allow_ignore_quota=True)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -665,7 +733,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
def test_voucher_quota_block(self):
|
||||
self.quota_tickets.size = 1
|
||||
self.quota_tickets.save()
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
block_quota=True)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -684,7 +752,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(objs[0].price, Decimal('12.00'))
|
||||
|
||||
def test_voucher_doubled(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event)
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
'_voucher_code': v.code,
|
||||
@@ -758,7 +826,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(len(objs), 0)
|
||||
|
||||
def test_voucher_multiuse_ok(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=0)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '2',
|
||||
@@ -769,7 +837,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert all(cp.voucher == v for cp in positions)
|
||||
|
||||
def test_voucher_multiuse_multiprod_ok(self):
|
||||
v = Voucher.objects.create(quota=self.quota_all, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(quota=self.quota_all, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=0)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -781,7 +849,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert all(cp.voucher == v for cp in positions)
|
||||
|
||||
def test_voucher_multiuse_partially(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=1)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '2',
|
||||
@@ -793,7 +861,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert not positions.exists()
|
||||
|
||||
def test_voucher_multiuse_multiprod_partially(self):
|
||||
v = Voucher.objects.create(quota=self.quota_all, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(quota=self.quota_all, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=1)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -807,7 +875,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert all(cp.voucher == v for cp in positions)
|
||||
|
||||
def test_voucher_multiuse_redeemed(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=2)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '2',
|
||||
@@ -819,7 +887,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert not positions.exists()
|
||||
|
||||
def test_voucher_multiuse_multiprod_redeemed(self):
|
||||
v = Voucher.objects.create(quota=self.quota_all, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(quota=self.quota_all, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=2)
|
||||
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||
'item_%d' % self.ticket.id: '1',
|
||||
@@ -832,7 +900,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert not positions.exists()
|
||||
|
||||
def test_voucher_multiuse_redeemed_in_my_cart(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
expires=now() - timedelta(minutes=10), item=self.ticket, voucher=v, price=Decimal('12.00'),
|
||||
@@ -848,7 +916,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert positions.count() == 1
|
||||
|
||||
def test_voucher_multiuse_redeemed_in_other_cart(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
expires=now() + timedelta(minutes=10), item=self.ticket, voucher=v, price=Decimal('12.00'),
|
||||
@@ -864,7 +932,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
assert not positions.exists()
|
||||
|
||||
def test_voucher_multiuse_redeemed_in_other_expired_cart(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
max_usages=2, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
expires=now() - timedelta(minutes=10), item=self.ticket, voucher=v, price=Decimal('12.00'),
|
||||
|
||||
@@ -294,7 +294,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.assertEqual(cr1.price, 24)
|
||||
|
||||
def test_voucher(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2))
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -312,7 +312,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.assertEqual(Voucher.objects.get(pk=v.pk).redeemed, 1)
|
||||
|
||||
def test_voucher_required(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2))
|
||||
self.ticket.require_voucher = True
|
||||
self.ticket.save()
|
||||
@@ -341,7 +341,7 @@ class CheckoutTestCase(TestCase):
|
||||
assert doc.select(".alert-danger")
|
||||
|
||||
def test_voucher_price_changed(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2))
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -355,20 +355,8 @@ class CheckoutTestCase(TestCase):
|
||||
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||
self.assertEqual(cr1.price, Decimal('12.00'))
|
||||
|
||||
def test_voucher_expired(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() - timedelta(days=2))
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=12, expires=now() - timedelta(minutes=10), voucher=v
|
||||
)
|
||||
self._set_session('payment', 'banktransfer')
|
||||
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||
self.assertIn("expired", doc.select(".alert-danger")[0].text)
|
||||
|
||||
def test_voucher_redeemed(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -380,7 +368,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.assertIn("has already been", doc.select(".alert-danger")[0].text)
|
||||
|
||||
def test_voucher_multiuse_redeemed(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), max_usages=3, redeemed=3)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -392,7 +380,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.assertIn("has already been", doc.select(".alert-danger")[0].text)
|
||||
|
||||
def test_voucher_multiuse_partially(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), max_usages=3, redeemed=2)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -409,7 +397,7 @@ class CheckoutTestCase(TestCase):
|
||||
assert CartPosition.objects.filter(cart_id=self.session_key).count() == 1
|
||||
|
||||
def test_voucher_multiuse_ok(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -430,7 +418,7 @@ class CheckoutTestCase(TestCase):
|
||||
assert v.redeemed == 3
|
||||
|
||||
def test_voucher_multiuse_in_other_cart_expired(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id='other', item=self.ticket,
|
||||
@@ -455,7 +443,7 @@ class CheckoutTestCase(TestCase):
|
||||
assert v.redeemed == 3
|
||||
|
||||
def test_voucher_multiuse_in_other_cart(self):
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), max_usages=3, redeemed=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id='other', item=self.ticket,
|
||||
@@ -478,7 +466,7 @@ class CheckoutTestCase(TestCase):
|
||||
def test_voucher_ignore_quota(self):
|
||||
self.quota_tickets.size = 0
|
||||
self.quota_tickets.save()
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), allow_ignore_quota=True)
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -496,7 +484,7 @@ class CheckoutTestCase(TestCase):
|
||||
def test_voucher_block_quota(self):
|
||||
self.quota_tickets.size = 1
|
||||
self.quota_tickets.save()
|
||||
v = Voucher.objects.create(item=self.ticket, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), block_quota=True)
|
||||
cr1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
@@ -523,7 +511,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.quota_tickets.save()
|
||||
q2 = self.event.quotas.create(name='Testquota', size=0)
|
||||
q2.items.add(self.ticket)
|
||||
v = Voucher.objects.create(quota=self.quota_tickets, price=Decimal('12.00'), event=self.event,
|
||||
v = Voucher.objects.create(quota=self.quota_tickets, value=Decimal('12.00'), event=self.event,
|
||||
valid_until=now() + timedelta(days=2), block_quota=True)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
|
||||
@@ -285,19 +285,27 @@ class VoucherRedeemItemDisplayTest(EventTestMixin, SoupTest):
|
||||
html = self.client.get('/%s/%s/redeem?voucher=%s' % (self.orga.slug, self.event.slug, self.v.code))
|
||||
assert "_voucher_item" in html.rendered_content
|
||||
|
||||
def test_special_price(self):
|
||||
self.v.price = Decimal("10.00")
|
||||
def test_voucher_price(self):
|
||||
self.v.value = Decimal("10.00")
|
||||
self.v.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
|
||||
assert "10.00" in html.rendered_content
|
||||
|
||||
def test_special_price_variations(self):
|
||||
def test_voucher_price_percentage(self):
|
||||
self.v.value = Decimal("10.00")
|
||||
self.v.price_mode = 'percent'
|
||||
self.v.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
|
||||
assert "10.80" in html.rendered_content
|
||||
|
||||
def test_voucher_price_variations(self):
|
||||
var1 = ItemVariation.objects.create(item=self.item, value='Red', default_price=14, position=1)
|
||||
var2 = ItemVariation.objects.create(item=self.item, value='Black', position=2)
|
||||
self.q.variations.add(var1)
|
||||
self.q.variations.add(var2)
|
||||
self.v.price = Decimal("10.00")
|
||||
self.v.value = Decimal("10.00")
|
||||
self.v.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
|
||||
|
||||
Reference in New Issue
Block a user