diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index c9ddf51a9a..66eaa0b1fc 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -53,6 +53,7 @@ from django.utils.translation import ( ) from django_scopes import scopes_disabled +from pretix.base.decimal import round_decimal from pretix.base.i18n import language from pretix.base.media import MEDIA_TYPES from pretix.base.models import ( @@ -916,6 +917,8 @@ class CartManager: if custom_price > 99_999_999_999: raise CartError(error_messages['price_too_high']) + custom_price = round_decimal(custom_price, currency=self.event.currency) + op = self.AddOperation( count=i['count'], item=item, @@ -1038,6 +1041,8 @@ class CartManager: if custom_price > 99_999_999_999: raise CartError(error_messages['price_too_high']) + custom_price = round_decimal(custom_price, currency=self.event.currency) + # Fix positions with wrong price (TODO: happens out-of-cartmanager-transaction and therefore a little hacky) for ca in current_addons[cp][a['item'], a['variation']]: if ca.listed_price != listed_price: diff --git a/src/tests/presale/test_cart.py b/src/tests/presale/test_cart.py index ab0a852fcd..72eb9661f3 100644 --- a/src/tests/presale/test_cart.py +++ b/src/tests/presale/test_cart.py @@ -697,6 +697,42 @@ class CartTest(CartTestMixin, TestCase): self.assertIsNone(objs[0].variation) self.assertEqual(objs[0].price, 23) + def test_free_price_rounding(self): + 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: '40.1234', + }, follow=True) + self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), + target_status_code=200) + + with scopes_disabled(): + cr1 = CartPosition.objects.get() + assert cr1.listed_price == Decimal('23.00') + assert cr1.custom_price_input == Decimal('40.12') + assert cr1.price == Decimal('40.12') + + def test_free_price_rounding_jpy(self): + self.event.currency = "JPY" + self.event.save() + 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: '40.1234', + }, follow=True) + self.assertRedirects(response, '/%s/%s/?require_cookie=true' % (self.orga.slug, self.event.slug), + target_status_code=200) + + with scopes_disabled(): + cr1 = CartPosition.objects.get() + assert cr1.listed_price == Decimal('23.00') + assert cr1.custom_price_input == Decimal('40.00') + assert cr1.price == Decimal('40.00') + def test_variation_inactive(self): self.shirt_red.active = False self.shirt_red.save() @@ -3060,6 +3096,31 @@ class CartAddonTest(CartTestMixin, TestCase): assert cp1.addons.count() == 3 assert all(a.price == Decimal('12.00') for a in cp1.addons.all()) + @classscope(attr='orga') + def test_free_price_rounding(self): + self.event.settings.locales = ['de'] + self.event.settings.locale = 'de' + self.event.currency = "JPY" + self.event.save() + + self.workshop1.free_price = True + self.workshop1.save() + cp1 = CartPosition.objects.create( + event=self.event, cart_id=self.session_key, item=self.ticket, + price=23, expires=now() - timedelta(minutes=10) + ) + + response = self.client.post('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), { + 'cp_{}_item_{}'.format(cp1.pk, self.workshop1.pk): '1', + 'cp_{}_item_{}_price'.format(cp1.pk, self.workshop1.pk): '99,99', + }, follow=True) + self.assertRedirects(response, '/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), + target_status_code=200) + with scopes_disabled(): + assert cp1.addons.count() == 1 + assert cp1.addons.first().item == self.workshop1 + assert cp1.addons.first().price == Decimal('100') + @classscope(attr='orga') def test_change_number(self): cp1 = CartPosition.objects.create(