Fix voucher application on "free price" items (Z#23183254) (#4863)

* Do not apply vouchers on "free price" items where more than minimum price is selected

* Do apply vouchers on "free price" items if exactly the minimum price is selected

* Update cart.py

* Add test cases, fix bug in adjacent test

* Fix code style

---------

Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
Mira
2025-02-24 17:14:49 +01:00
committed by GitHub
parent 1af7636aa3
commit fb6117a307
2 changed files with 71 additions and 2 deletions

View File

@@ -624,6 +624,9 @@ class CartManager:
if p.is_bundled:
continue
if p.custom_price_input and p.custom_price_input != p.listed_price:
continue
if p.listed_price is None:
if p.addon_to_id and is_included_for_free(p.item, p.addon_to):
listed_price = Decimal('0.00')
@@ -1346,8 +1349,10 @@ class CartManager:
op.position.price_after_voucher = op.price_after_voucher
op.position.voucher = op.voucher
if op.position.custom_price_input and op.position.custom_price_input == op.position.listed_price:
op.position.custom_price_input = op.price_after_voucher
# op.position.price will be set in recompute_final_prices_and_taxes
op.position.save(update_fields=['price_after_voucher', 'voucher'])
op.position.save(update_fields=['price_after_voucher', 'voucher', 'custom_price_input'])
vouchers_ok[op.voucher] -= 1
if op.voucher.all_bundles_included or op.voucher.all_addons_included:

View File

@@ -1685,7 +1685,7 @@ class CartTest(CartTestMixin, TestCase):
def test_voucher_free_price_lower_bound(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 = False
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',
@@ -1708,6 +1708,70 @@ class CartTest(CartTestMixin, TestCase):
self.assertEqual(objs[0].listed_price, Decimal('23.00'))
self.assertEqual(objs[0].price_after_voucher, Decimal('20.70'))
def test_voucher_free_price_redeem_lowers_if_min(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()
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'price_%d' % self.ticket.id: '23.00',
}, follow=True)
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('23.00'))
self.assertEqual(objs[0].listed_price, Decimal('23.00'))
self.assertEqual(objs[0].price_after_voucher, Decimal('23.00'))
response = self.client.post('/%s/%s/cart/voucher' % (self.orga.slug, self.event.slug),
{'voucher': 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('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)
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('20.70'))
self.assertEqual(objs[0].listed_price, Decimal('23.00'))
self.assertEqual(objs[0].price_after_voucher, Decimal('20.70'))
def test_voucher_free_price_redeem_keeps_if_not_min(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()
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
'item_%d' % self.ticket.id: '1',
'price_%d' % self.ticket.id: '25.00',
}, follow=True)
response = self.client.post('/%s/%s/cart/voucher' % (self.orga.slug, self.event.slug),
{'voucher': v.code},
follow=True)
doc = BeautifulSoup(response.rendered_content, "lxml")
self.assertIn('We did not find any position in your cart that we could use this voucher for', doc.select('.alert-danger')[0].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('25.00'))
self.assertEqual(objs[0].listed_price, Decimal('23.00'))
self.assertEqual(objs[0].price_after_voucher, Decimal('23.00'))
def test_voucher_redemed(self):
with scopes_disabled():
v = Voucher.objects.create(item=self.ticket, value=Decimal('12.00'), event=self.event, redeemed=1)