forked from CGM_Public/pretix_original
Add a maximum budget to vouchers (#1526)
* Data model changes * Fix test failures * Adjustments * Some tests and API support * Check when extending orders * Make things more deterministic, fix style * Do not apply negative discounts * Update price_before_voucher on item/subevent changes * Add tests for price_before_voucher in combination with free price * Fix InvoiceAddress.DoesNotExist
This commit is contained in:
@@ -1388,6 +1388,32 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(objs[0].item, self.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('21.00'))
|
||||
self.assertEqual(objs[0].price_before_voucher, Decimal('23.00'))
|
||||
|
||||
def test_voucher_free_price_before_voucher_cap(self):
|
||||
with scopes_disabled():
|
||||
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: '41.00',
|
||||
'_voucher_code': v.code,
|
||||
}, follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (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('41', doc.select('.cart .cart-row')[0].select('.price')[0].text)
|
||||
self.assertIn('41', doc.select('.cart .cart-row')[0].select('.price')[1].text)
|
||||
with scopes_disabled():
|
||||
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('41.00'))
|
||||
self.assertEqual(objs[0].price_before_voucher, Decimal('41.00'))
|
||||
|
||||
def test_voucher_free_price_lower_bound(self):
|
||||
with scopes_disabled():
|
||||
@@ -1412,6 +1438,7 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertEqual(objs[0].item, self.ticket)
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, Decimal('20.70'))
|
||||
self.assertEqual(objs[0].price_before_voucher, Decimal('23.00'))
|
||||
|
||||
def test_voucher_redemed(self):
|
||||
with scopes_disabled():
|
||||
@@ -2438,6 +2465,20 @@ class CartAddonTest(CartTestMixin, TestCase):
|
||||
assert cp2.expires > now()
|
||||
assert cp2.addon_to_id == cp1.pk
|
||||
|
||||
@classscope(attr='orga')
|
||||
def test_expand_expired_refresh_voucher(self):
|
||||
v = Voucher.objects.create(item=self.ticket, value=Decimal('20.00'), event=self.event, price_mode='set',
|
||||
valid_until=now() + timedelta(days=2), max_usages=999, redeemed=0)
|
||||
cp1 = CartPosition.objects.create(
|
||||
expires=now() - timedelta(minutes=10), item=self.ticket, price=Decimal('21.50'),
|
||||
event=self.event, cart_id=self.session_key, voucher=v
|
||||
)
|
||||
self.cm.extend_expired_positions()
|
||||
self.cm.commit()
|
||||
cp1.refresh_from_db()
|
||||
assert cp1.expires > now()
|
||||
assert cp1.price_before_voucher == Decimal('23.00')
|
||||
|
||||
|
||||
class CartBundleTest(CartTestMixin, TestCase):
|
||||
@scopes_disabled()
|
||||
@@ -2490,6 +2531,30 @@ class CartBundleTest(CartTestMixin, TestCase):
|
||||
assert cp.price == 23 - 1.5
|
||||
assert cp.addons.count() == 1
|
||||
assert cp.voucher == v
|
||||
assert cp.price_before_voucher == 23 - 1.5
|
||||
a = cp.addons.get()
|
||||
assert a.item == self.trans
|
||||
assert a.price == 1.5
|
||||
assert not a.voucher
|
||||
|
||||
@classscope(attr='orga')
|
||||
def test_discounted_voucher_on_base_product(self):
|
||||
v = self.event.vouchers.create(code="foo", item=self.ticket, price_mode='subtract', value=Decimal('1.50'))
|
||||
self.cm.add_new_items([
|
||||
{
|
||||
'item': self.ticket.pk,
|
||||
'variation': None,
|
||||
'voucher': v.code,
|
||||
'count': 1
|
||||
}
|
||||
])
|
||||
self.cm.commit()
|
||||
cp = CartPosition.objects.get(addon_to__isnull=True)
|
||||
assert cp.item == self.ticket
|
||||
assert cp.price == 23 - 1.5 - 1.5
|
||||
assert cp.addons.count() == 1
|
||||
assert cp.voucher == v
|
||||
assert cp.price_before_voucher == 23 - 1.5
|
||||
a = cp.addons.get()
|
||||
assert a.item == self.trans
|
||||
assert a.price == 1.5
|
||||
|
||||
@@ -3000,3 +3000,140 @@ class CheckoutSeatingTest(BaseCheckoutTestCase, TestCase):
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {}, 'web')
|
||||
assert not CartPosition.objects.filter(pk=self.cp1.pk).exists()
|
||||
|
||||
|
||||
class CheckoutVoucherBudgetTest(BaseCheckoutTestCase, TestCase):
|
||||
@scopes_disabled()
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.v = Voucher.objects.create(item=self.ticket, value=Decimal('21.50'), event=self.event, price_mode='set',
|
||||
valid_until=now() + timedelta(days=2), max_usages=999, redeemed=0)
|
||||
self.cp1 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price_before_voucher=23, price=21.5, expires=now() + timedelta(minutes=10), voucher=self.v
|
||||
)
|
||||
self.cp2 = CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price_before_voucher=23, price=21.5, expires=now() + timedelta(minutes=10), voucher=self.v
|
||||
)
|
||||
|
||||
@scopes_disabled()
|
||||
def test_no_budget(self):
|
||||
oid = _perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
o = Order.objects.get(pk=oid)
|
||||
op = o.positions.first()
|
||||
assert op.item == self.ticket
|
||||
assert op.price_before_voucher == Decimal('23.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_for_second_order(self):
|
||||
self.v.budget = Decimal('1.50')
|
||||
self.v.save()
|
||||
oid = _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
o = Order.objects.get(pk=oid)
|
||||
op = o.positions.first()
|
||||
assert op.item == self.ticket
|
||||
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('23.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_between_positions(self):
|
||||
self.v.budget = Decimal('1.50')
|
||||
self.v.save()
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp1.refresh_from_db()
|
||||
assert self.cp1.price == Decimal('21.50')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('23.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_in_first_position(self):
|
||||
self.v.budget = Decimal('1.00')
|
||||
self.v.save()
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp1.refresh_from_db()
|
||||
assert self.cp1.price == Decimal('22.00')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('23.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_in_second_position(self):
|
||||
self.v.budget = Decimal('2.50')
|
||||
self.v.save()
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp1.refresh_from_db()
|
||||
assert self.cp1.price == Decimal('21.50')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('22.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_during_price_change(self):
|
||||
self.v.budget = Decimal('2.50')
|
||||
self.v.value = Decimal('21.00')
|
||||
self.v.save()
|
||||
self.cp1.expires = now() - timedelta(hours=1)
|
||||
self.cp1.save()
|
||||
self.cp2.expires = now() - timedelta(hours=1)
|
||||
self.cp2.save()
|
||||
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp1.refresh_from_db()
|
||||
assert self.cp1.price == Decimal('21.00')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('22.50')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_exceeded_expired_cart(self):
|
||||
self.v.budget = Decimal('0.00')
|
||||
self.v.value = Decimal('21.00')
|
||||
self.v.save()
|
||||
self.cp1.expires = now() - timedelta(hours=1)
|
||||
self.cp1.save()
|
||||
self.cp2.expires = now() - timedelta(hours=1)
|
||||
self.cp2.save()
|
||||
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp1.pk, self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp1.refresh_from_db()
|
||||
assert self.cp1.price == Decimal('23.00')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('23.00')
|
||||
|
||||
@scopes_disabled()
|
||||
def test_budget_overbooked_expired_cart(self):
|
||||
self.v.budget = Decimal('1.50')
|
||||
self.v.value = Decimal('21.50')
|
||||
self.v.save()
|
||||
self.cp1.expires = now() - timedelta(hours=1)
|
||||
self.cp1.save()
|
||||
self.cp2.expires = now() - timedelta(hours=1)
|
||||
self.cp2.save()
|
||||
oid = _perform_order(self.event, 'manual', [self.cp1.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
o = Order.objects.get(pk=oid)
|
||||
op = o.positions.first()
|
||||
|
||||
assert op.item == self.ticket
|
||||
self.v.budget = Decimal('1.00')
|
||||
self.v.save()
|
||||
|
||||
with self.assertRaises(OrderError):
|
||||
_perform_order(self.event, 'manual', [self.cp2.pk], 'admin@example.org', 'en', None, {},
|
||||
'web')
|
||||
self.cp2.refresh_from_db()
|
||||
assert self.cp2.price == Decimal('23.00')
|
||||
|
||||
Reference in New Issue
Block a user