lingo: accept french amount format (#35052)

This commit is contained in:
Thomas NOËL 2019-07-25 15:20:41 +02:00
parent 73a7643788
commit ff6878c39d
2 changed files with 58 additions and 15 deletions

View File

@ -26,7 +26,7 @@ from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.http import HttpResponseForbidden, Http404, JsonResponse
from django.template.response import TemplateResponse
from django.utils import timezone, dateparse
from django.utils import timezone, dateparse, six
from django.utils.encoding import force_text
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View, DetailView, ListView, TemplateView
@ -72,6 +72,14 @@ def lingo_check_request_signature(request):
return check_request_signature(request, keys=keys)
class LocaleDecimal(Decimal):
# accept , instead of . for French users comfort
def __new__(cls, value="0", *args, **kwargs):
if isinstance(value, six.string_types) and settings.LANGUAGE_CODE.startswith('fr-'):
value = value.replace(',', '.')
return super(LocaleDecimal, cls).__new__(cls, value, *args, **kwargs)
class RegiesApiView(ListView):
model = Regie
@ -94,9 +102,9 @@ class AddBasketItemApiView(View):
def get_amount(self, amount):
if isinstance(amount, list):
d = Decimal(sum([Decimal(a) for a in amount]))
d = Decimal(sum([LocaleDecimal(a) for a in amount]))
else:
d = Decimal(amount)
d = LocaleDecimal(amount)
return d.quantize(Decimal('0.01'), ROUND_HALF_UP)
def post(self, request, *args, **kwargs):
@ -111,13 +119,22 @@ class AddBasketItemApiView(View):
raise Exception('missing amount parameter')
item = BasketItem(amount=0)
item.amount = self.get_amount(request.GET.getlist('amount'))
try:
item.amount = self.get_amount(request.GET.getlist('amount'))
except ArithmeticError:
return HttpResponseBadRequest('invalid value for "amount" in query string')
if request_body.get('amount'):
item.amount += self.get_amount(request_body['amount'])
try:
item.amount += self.get_amount(request_body['amount'])
except ArithmeticError:
return HttpResponseBadRequest('invalid value for "amount" in payload')
if extra.get('amount'):
item.amount += self.get_amount(extra['amount'])
try:
item.amount += self.get_amount(extra['amount'])
except ArithmeticError:
return HttpResponseBadRequest('invalid value for "amount" in extra payload')
if 'extra' in request_body:
item.request_data = request_body.get('extra')
@ -234,11 +251,11 @@ class ValidateTransactionApiView(View):
raise Http404
payment = get_eopayment_object(request, transaction.regie)
amount = request.GET['amount']
amount = LocaleDecimal(request.GET['amount'])
logger.info(u'validating amount %s for transaction %s', amount, smart_text(transaction.id))
try:
result = payment.backend.validate(Decimal(amount), transaction.bank_data)
result = payment.backend.validate(amount, transaction.bank_data)
except eopayment.ResponseError as e:
logger.error(u'failed in validation operation: %s', e)
response = HttpResponse(content_type='application/json')
@ -247,7 +264,7 @@ class ValidateTransactionApiView(View):
logger.info(u'bank validation result: %r', result)
operation = TransactionOperation(transaction=transaction,
kind='validation', amount=Decimal(amount), bank_result=result)
kind='validation', amount=amount, bank_result=result)
operation.save()
response = HttpResponse(content_type='application/json')
@ -275,11 +292,11 @@ class CancelTransactionApiView(View):
raise Http404
payment = get_eopayment_object(request, transaction.regie)
amount = request.GET['amount']
amount = LocaleDecimal(request.GET['amount'])
logger.info(u'cancelling amount %s for transaction %s', amount, smart_text(transaction.id))
try:
result = payment.backend.cancel(Decimal(amount), transaction.bank_data)
result = payment.backend.cancel(amount, transaction.bank_data)
except eopayment.ResponseError as e:
logger.error(u'failed in cancel operation: %s', e)
response = HttpResponse(content_type='application/json')
@ -288,7 +305,7 @@ class CancelTransactionApiView(View):
logger.info(u'bank cancellation result: %r', result)
operation = TransactionOperation(transaction=transaction,
kind='cancellation', amount=Decimal(amount), bank_result=result)
kind='cancellation', amount=amount, bank_result=result)
operation.save()
response = HttpResponse(content_type='application/json')
@ -710,11 +727,11 @@ class SelfInvoiceView(View):
except SelfDeclaredInvoicePayment.DoesNotExist:
raise Http404()
invoice_id = request.GET.get('invoice-number', '')
invoice_amount = request.GET.get('invoice-amount', '').replace(',', '.')
invoice_amount = request.GET.get('invoice-amount', '')
msg = None
url = None
try:
invoice_amount = Decimal(invoice_amount)
invoice_amount = LocaleDecimal(invoice_amount)
except ArithmeticError:
invoice_amount = '-'
msg = _('Sorry, the provided amount is invalid.')

View File

@ -12,6 +12,7 @@ from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.wsgi import get_wsgi_application
from django.conf import settings
from django.test import override_settings
from django.utils import timezone
from django.utils.six.moves.urllib import parse as urlparse
from django.contrib.messages.storage.session import SessionStorage
@ -238,6 +239,31 @@ def test_add_amount_to_basket(app, key, regie, user):
assert json.loads(resp.content)['result'] == 'success'
assert BasketItem.objects.filter(amount=Decimal('81.22')).exists()
# accept french notation if settings.LANGUAGE_CODE is 'fr-*'
url = '%s?amount=10,00&email=%s&orig=wcs' % (reverse('api-add-basket-item'), user_email)
url = sign_url(url, key)
resp = app.post_json(url, params=data, status=400)
assert 'invalid value for "amount" in query string' in resp.content
data['amount'] = '1,10'
url = '%s?amount=10.00&email=%s&orig=wcs' % (reverse('api-add-basket-item'), user_email)
url = sign_url(url, key)
resp = app.post_json(url, params=data, status=400)
assert 'invalid value for "amount" in payload' in resp.content
data['amount'] = '1.10'
data['extra'] = {'amount': '0,01'}
resp = app.post_json(url, params=data, status=400)
assert 'invalid value for "amount" in extra payload' in resp.content
data['amount'] = '1,10'
data['extra'] = {'amount': '0,01'}
url = '%s?amount=10,00&email=%s&orig=wcs' % (reverse('api-add-basket-item'), user_email)
url = sign_url(url, key)
with override_settings(LANGUAGE_CODE='fr-be'):
resp = app.post_json(url, params=data, status=200)
assert resp.status_code == 200
assert json.loads(resp.content)['result'] == 'success'
assert BasketItem.objects.filter(amount=Decimal('11.11')).exists()
other_regie.is_default = True
other_regie.save()
data['amount'] = []
@ -408,7 +434,7 @@ def test_pay_multiple_regies(app, key, regie, user):
resp = resp.forms[0].submit()
assert resp.location.startswith('http://dummy-payment.demo.entrouvert.com/')
qs = urlparse.parse_qs(urlparse.urlparse(resp.location).query)
assert qs['amount'] == ['223.35']
assert qs['amount'] == ['234.46']
resp = login(app).get(page.get_online_url())
resp = resp.forms[1].submit()