forked from CGM_Public/pretix_original
Cart: ensure free price input is decimal (PRETIXEU-80N)
Co-authored-by: Phin Wolkwitz <wolkwitz@rami.io>
This commit is contained in:
committed by
GitHub
parent
5ad0f92776
commit
e9b22b7d33
@@ -31,6 +31,7 @@
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
import re
|
||||
import uuid
|
||||
from collections import Counter, defaultdict, namedtuple
|
||||
from datetime import datetime, time, timedelta
|
||||
@@ -38,6 +39,7 @@ from decimal import Decimal
|
||||
from typing import List, Optional
|
||||
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import DatabaseError, transaction
|
||||
from django.db.models import Count, Exists, IntegerField, OuterRef, Q, Value
|
||||
@@ -135,6 +137,7 @@ error_messages = {
|
||||
'some_subevent_ended': gettext_lazy(
|
||||
'The booking period for one of the events in your cart has ended. The affected '
|
||||
'positions have been removed from your cart.'),
|
||||
'price_not_a_number': gettext_lazy('The entered price is not a number.'),
|
||||
'price_too_high': gettext_lazy('The entered price is to high.'),
|
||||
'voucher_invalid': gettext_lazy('This voucher code is not known in our database.'),
|
||||
'voucher_min_usages': gettext_lazy(
|
||||
@@ -725,9 +728,18 @@ class CartManager:
|
||||
price_after_voucher = listed_price
|
||||
custom_price = None
|
||||
if item.free_price and i.get('price'):
|
||||
custom_price = Decimal(str(i.get('price')).replace(",", "."))
|
||||
custom_price = re.sub('[^0-9.,]', '', str(i.get('price')))
|
||||
if not custom_price:
|
||||
raise CartError(error_messages['price_not_a_number'])
|
||||
try:
|
||||
custom_price = forms.DecimalField(localize=True).to_python(custom_price)
|
||||
except:
|
||||
try:
|
||||
custom_price = Decimal(custom_price)
|
||||
except:
|
||||
raise CartError(error_messages['price_not_a_number'])
|
||||
if custom_price > 99_999_999_999:
|
||||
raise ValueError('price_too_high')
|
||||
raise CartError(error_messages['price_too_high'])
|
||||
|
||||
op = self.AddOperation(
|
||||
count=i['count'],
|
||||
@@ -840,9 +852,18 @@ class CartManager:
|
||||
listed_price = get_listed_price(item, variation, cp.subevent)
|
||||
custom_price = None
|
||||
if item.free_price and a.get('price'):
|
||||
custom_price = Decimal(str(a.get('price')).replace(",", "."))
|
||||
custom_price = re.sub('[^0-9.,]', '', a.get('price'))
|
||||
if not custom_price:
|
||||
raise CartError(error_messages['price_not_a_number'])
|
||||
try:
|
||||
custom_price = forms.DecimalField(localize=True).to_python(custom_price)
|
||||
except:
|
||||
try:
|
||||
custom_price = Decimal(custom_price)
|
||||
except:
|
||||
raise CartError(error_messages['price_not_a_number'])
|
||||
if custom_price > 99_999_999_999:
|
||||
raise ValueError('price_too_high')
|
||||
raise CartError(error_messages['price_too_high'])
|
||||
|
||||
# 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']]:
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
from django.utils.timezone import now
|
||||
|
||||
@@ -69,7 +71,16 @@ def get_price(item: Item, variation: ItemVariation = None,
|
||||
subtract_from_gross=bundled_sum)
|
||||
elif item.free_price and custom_price is not None and custom_price != "":
|
||||
if not isinstance(custom_price, Decimal):
|
||||
custom_price = Decimal(str(custom_price).replace(",", "."))
|
||||
custom_price = re.sub('[^0-9.,]', '', str(custom_price))
|
||||
if not custom_price:
|
||||
raise ValueError('price_not_a_number')
|
||||
try:
|
||||
custom_price = forms.DecimalField(localize=True).to_python(custom_price)
|
||||
except:
|
||||
try:
|
||||
custom_price = Decimal(custom_price)
|
||||
except:
|
||||
raise ValueError('price_not_a_number')
|
||||
if custom_price > 99_999_999_999:
|
||||
raise ValueError('price_too_high')
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import json
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.utils import translation
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
|
||||
@@ -196,7 +197,8 @@ def test_free_price_accepted(item):
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_string(item):
|
||||
item.free_price = True
|
||||
assert get_price(item, custom_price='42,00').gross == Decimal('42.00')
|
||||
with translation.override('de'):
|
||||
assert get_price(item, custom_price='42,00').gross == Decimal('42.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -209,7 +211,7 @@ def test_free_price_float(item):
|
||||
def test_free_price_limit(item):
|
||||
item.free_price = True
|
||||
with pytest.raises(ValueError):
|
||||
get_price(item, custom_price=Decimal('200000000'))
|
||||
get_price(item, custom_price=Decimal('200000000000'))
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -635,6 +635,21 @@ class CartTest(CartTestMixin, TestCase):
|
||||
self.assertIsNone(objs[0].variation)
|
||||
self.assertEqual(objs[0].price, 24)
|
||||
|
||||
def test_free_price_numeric(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: 'abcde'
|
||||
}, 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('not a number', doc.select('#error-message')[0].text)
|
||||
with scopes_disabled():
|
||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||
self.assertEqual(len(objs), 0)
|
||||
|
||||
def test_free_price_only_if_allowed(self):
|
||||
self.ticket.free_price = False
|
||||
self.ticket.save()
|
||||
|
||||
@@ -3138,6 +3138,9 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
||||
assert cp1.addons.last().item == self.workshop1
|
||||
|
||||
def test_set_addon_free_price(self):
|
||||
self.event.settings.locales = ['de']
|
||||
self.event.settings.locale = 'de'
|
||||
|
||||
with scopes_disabled():
|
||||
self.workshop1.free_price = True
|
||||
self.workshop1.save()
|
||||
|
||||
Reference in New Issue
Block a user