diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 1ec6981055..1263a24c00 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -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: diff --git a/src/tests/presale/test_cart.py b/src/tests/presale/test_cart.py index b4af8845ac..d1d7a901ff 100644 --- a/src/tests/presale/test_cart.py +++ b/src/tests/presale/test_cart.py @@ -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)