Vouchers: Allow to set all addons or bundles as included (#3322)

Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
Raphael Michel
2023-05-22 11:59:27 +02:00
committed by GitHub
parent 5eff9a86f4
commit c75c080c5c
19 changed files with 241 additions and 23 deletions

View File

@@ -79,6 +79,8 @@ TEST_VOUCHER_RES = {
'tag': 'Foo',
'comment': '',
'show_hidden_items': True,
'all_addons_included': False,
'all_bundles_included': False,
'subevent': None,
'seat': None,
}

View File

@@ -28,7 +28,7 @@ from django.utils.timezone import now
from django_scopes import scopes_disabled
from pretix.base.models import (
CartPosition, Event, Item, OrderPosition, Organizer, Quota,
CartPosition, Event, Item, OrderPosition, Organizer, Quota, Voucher,
)
from pretix.base.services.orders import _perform_order
from pretix.testutils.sessions import get_cart_session_key
@@ -116,6 +116,47 @@ class BundlePricesTest(TestCase):
assert op2.item == self.food
assert op2.tax_rate == Decimal('7.00')
def test_voucher_includes_bundles(self):
with scopes_disabled():
v = Voucher.objects.create(item=self.ticket, value=Decimal('0.00'), event=self.event, price_mode='set',
all_bundles_included=True)
# Verify correct price displayed on event page
response = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
self.assertContains(response, '23.00')
# Verify correct price being added to cart
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)
with scopes_disabled():
cp1 = CartPosition.objects.get(is_bundled=False)
cp2 = CartPosition.objects.get(is_bundled=True)
assert cp1.price == Decimal('0.00')
assert cp1.item == self.ticket
assert cp2.price == Decimal('0.00')
assert cp2.item == self.food
# Make sure cart expires
cp1.expires = now() - datetime.timedelta(minutes=120)
cp1.save()
cp2.expires = now() - datetime.timedelta(minutes=120)
cp2.save()
# Verify price is kept if cart expires and order is sent
with scopes_disabled():
_perform_order(self.event, self._manual_payment(), [cp1.pk, cp2.pk], 'admin@example.org', 'en', None, {}, 'web')
op1 = OrderPosition.objects.get(is_bundled=False)
op2 = OrderPosition.objects.get(is_bundled=True)
assert op1.price == Decimal('0.00')
assert op1.item == self.ticket
assert op1.tax_rate == Decimal('19.00')
assert op2.price == Decimal('0.00')
assert op2.item == self.food
assert op2.tax_rate == Decimal('7.00')
def test_net_price_definitions(self):
self.tr19.price_includes_tax = False
self.tr19.save()

View File

@@ -3119,6 +3119,27 @@ class CartBundleTest(CartTestMixin, TestCase):
assert a.item == self.trans
assert a.price == 1.5
@classscope(attr='orga')
def test_simple_bundled_voucher_all_free(self):
v = Voucher.objects.create(item=self.ticket, value=Decimal('0.00'), event=self.event, price_mode='set',
all_bundles_included=True)
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 == 0
assert cp.addons.count() == 1
a = cp.addons.get()
assert a.item == self.trans
assert a.price == 0
@classscope(attr='orga')
def test_voucher_on_base_product(self):
v = self.event.vouchers.create(code="foo", item=self.ticket)

View File

@@ -2263,6 +2263,29 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
with scopes_disabled():
self.assertEqual(OrderPosition.objects.filter(item=self.workshop1).last().price, 0)
def test_addon_price_included_in_voucher(self):
with scopes_disabled():
v = Voucher.objects.create(item=self.ticket, value=Decimal('0.00'), event=self.event, price_mode='set',
valid_until=now() + timedelta(days=2), all_addons_included=True)
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1,
price_included=False)
cp1 = CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=0, expires=now() - timedelta(minutes=10), voucher=v
)
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.workshop1,
price=0, expires=now() - timedelta(minutes=10),
addon_to=cp1
)
self._set_payment()
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
doc = BeautifulSoup(response.content.decode(), "lxml")
self.assertEqual(len(doc.select(".thank-you")), 1)
with scopes_disabled():
self.assertEqual(OrderPosition.objects.filter(item=self.workshop1).last().price, 0)
def test_confirm_price_changed_reverse_charge(self):
self._enable_reverse_charge()
self.ticket.default_price = 24
@@ -3970,6 +3993,27 @@ class CheckoutBundleTest(BaseCheckoutTestCase, TestCase):
assert a.item == self.trans
assert a.price == 1.5
@classscope(attr='orga')
def test_expired_bundle_with_voucher_bundles_included(self):
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, price_mode='none',
valid_until=now() + timedelta(days=2), all_bundles_included=True)
self.cp1.voucher = v
self.cp1.price = 23
self.cp1.expires = now() - timedelta(minutes=10)
self.cp1.save()
self.bundled1.price = 0
self.bundled1.expires = now() - timedelta(minutes=10)
self.bundled1.save()
oid = _perform_order(self.event, self._manual_payment(), [self.cp1.pk, self.bundled1.pk], 'admin@example.org', 'en', None, {}, 'web')
o = Order.objects.get(pk=oid['order_id'])
cp = o.positions.get(addon_to__isnull=True)
assert cp.item == self.ticket
assert cp.price == 23
assert cp.addons.count() == 1
a = cp.addons.get()
assert a.item == self.trans
assert a.price == 0
@classscope(attr='orga')
def test_expired_keep_price(self):
self.cp1.expires = now() - timedelta(minutes=10)

View File

@@ -650,6 +650,43 @@ class OrderChangeAddonsTest(BaseOrdersTest):
self.order.refresh_from_db()
assert self.order.total == Decimal('35.00')
def test_add_addon_included_in_voucher(self):
response = self.client.get(
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret)
)
assert response.status_code == 200
assert 'Workshop 1' in response.content.decode()
with scopes_disabled():
v = self.event.vouchers.create(item=self.ticket, all_addons_included=True)
self.ticket_pos.voucher = v
self.ticket_pos.save()
doc = BeautifulSoup(response.content.decode(), "lxml")
assert doc.select(f'input[name=cp_{self.ticket_pos.pk}_item_{self.workshop1.pk}]')
response = self.client.post(
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
{
f'cp_{self.ticket_pos.pk}_item_{self.workshop1.pk}': '1'
},
follow=True
)
doc = BeautifulSoup(response.content.decode(), "lxml")
form_data = extract_form_fields(doc.select('.main-box form')[0])
form_data['confirm'] = 'true'
response = self.client.post(
'/%s/%s/order/%s/%s/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret), form_data, follow=True
)
assert 'alert-success' in response.content.decode()
with scopes_disabled():
new_pos = self.ticket_pos.addons.get()
assert new_pos.item == self.workshop1
assert new_pos.price == Decimal('0.00')
self.order.refresh_from_db()
assert self.order.total == Decimal('23.00')
def test_add_addon_free_price(self):
self.workshop1.free_price = True
self.workshop1.save()