lingo: use form instead of json input for backends (#6710)
This commit is contained in:
parent
2892f355d4
commit
82261bf404
|
@ -18,22 +18,58 @@ import datetime
|
|||
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import Regie
|
||||
from .models import Regie, PaymentBackend
|
||||
|
||||
TYPE_FIELD_MAPPING = {
|
||||
str: forms.CharField,
|
||||
bool: forms.BooleanField,
|
||||
int: forms.IntegerField,
|
||||
float: forms.FloatField,
|
||||
}
|
||||
|
||||
|
||||
def get_validator(func, err_msg):
|
||||
def validate(value):
|
||||
if not func(value):
|
||||
message = err_msg or _('Invalid value.')
|
||||
raise ValidationError(message)
|
||||
return validate
|
||||
|
||||
|
||||
def create_form_fields(parameters, json_field):
|
||||
fields, initial = {}, {}
|
||||
for param in parameters:
|
||||
field_name = param['name']
|
||||
if param['type'] is bool:
|
||||
fields[field_name] = forms.BooleanField(label=param['caption'], required=False)
|
||||
initial[field_name] = param['default']
|
||||
if field_name in json_field:
|
||||
initial[field_name] = json_field[field_name]
|
||||
if field_name in ('normal_return_url', 'automatic_return_url') or param.get('deprecated'):
|
||||
continue
|
||||
|
||||
field_params = {
|
||||
'label': param.get('caption') or field_name,
|
||||
'required': param.get('required', False)
|
||||
}
|
||||
if 'validation' in param:
|
||||
field_params['validators'] = [
|
||||
get_validator(param['validation'], param.get('validation_err_msg'))
|
||||
]
|
||||
|
||||
_type = param.get('type', str)
|
||||
choices = param.get('choices')
|
||||
if choices is not None:
|
||||
field_class = forms.MultipleChoiceField if _type is list else forms.ChoiceField
|
||||
if choices and not isinstance(choices[0], tuple):
|
||||
choices = [(choice, choice) for choice in choices]
|
||||
field_params['choices'] = choices
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
field_class = TYPE_FIELD_MAPPING[_type]
|
||||
|
||||
fields[field_name] = field_class(**field_params)
|
||||
initial_value = json_field.get(field_name, param.get('default'))
|
||||
if initial_value:
|
||||
initial[field_name] = initial_value
|
||||
|
||||
return fields, initial
|
||||
|
||||
|
||||
|
@ -72,6 +108,33 @@ class RegieForm(forms.ModelForm):
|
|||
return instance
|
||||
|
||||
|
||||
class PaymentBackendForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = PaymentBackend
|
||||
fields = ['label', 'slug', 'service']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PaymentBackendForm, self).__init__(*args, **kwargs)
|
||||
fields, initial = create_form_fields(
|
||||
self.instance.get_payment().get_parameters(scope='global'),
|
||||
self.instance.service_options
|
||||
)
|
||||
self.fields.update(fields)
|
||||
self.initial.update(initial)
|
||||
if self.fields['service']:
|
||||
self.fields['service'].disabled = True
|
||||
|
||||
def save(self):
|
||||
instance = super(PaymentBackendForm, self).save()
|
||||
instance.service_options = compute_json_field(
|
||||
self.instance.get_payment().get_parameters(scope='global'),
|
||||
self.cleaned_data
|
||||
)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
class TransactionExportForm(forms.Form):
|
||||
start_date = forms.DateField(
|
||||
label=_('Start date'),
|
||||
|
|
|
@ -20,10 +20,12 @@ from dateutil import parser as date_parser
|
|||
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib import messages
|
||||
from django.db.models import Q, Prefetch
|
||||
from django.db.models.expressions import RawSQL
|
||||
from django.utils import six
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import CreateView, UpdateView, ListView, DeleteView, View
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponseRedirect
|
||||
|
@ -32,7 +34,7 @@ from django.template.response import TemplateResponse
|
|||
|
||||
import eopayment
|
||||
|
||||
from .forms import RegieForm
|
||||
from .forms import RegieForm, PaymentBackendForm
|
||||
from .forms import TransactionExportForm
|
||||
from .models import BasketItem, PaymentBackend, Regie, Transaction
|
||||
|
||||
|
@ -64,13 +66,16 @@ class PaymentBackendListView(ListView):
|
|||
|
||||
class PaymentBackendCreateView(CreateView):
|
||||
model = PaymentBackend
|
||||
fields = '__all__'
|
||||
success_url = reverse_lazy('lingo-manager-paymentbackend-list')
|
||||
fields = ['label', 'slug', 'service']
|
||||
|
||||
def get_success_url(self):
|
||||
messages.info(self.request, _('Please fill additional backend parameters.'))
|
||||
return reverse_lazy('lingo-manager-paymentbackend-edit', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
class PaymentBackendUpdateView(UpdateView):
|
||||
model = PaymentBackend
|
||||
fields = '__all__'
|
||||
form_class = PaymentBackendForm
|
||||
success_url = reverse_lazy('lingo-manager-paymentbackend-list')
|
||||
|
||||
|
||||
|
|
|
@ -88,11 +88,6 @@ def build_remote_item(data, regie):
|
|||
no_online_payment_reason=data.get('no_online_payment_reason'))
|
||||
|
||||
|
||||
def validate_dict(value):
|
||||
if not isinstance(value, dict):
|
||||
raise ValidationError(_('Value must be a JSON object'))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PaymentBackend(models.Model):
|
||||
label = models.CharField(verbose_name=_('Label'), max_length=64)
|
||||
|
@ -101,10 +96,7 @@ class PaymentBackend(models.Model):
|
|||
help_text=_('The identifier is used in webservice calls.'))
|
||||
service = models.CharField(
|
||||
verbose_name=_('Payment Service'), max_length=64, choices=SERVICES)
|
||||
service_options = JSONField(
|
||||
blank=True,
|
||||
verbose_name=_('Payment Service Options'),
|
||||
validators=[validate_dict])
|
||||
service_options = JSONField(blank=True, verbose_name=_('Payment Service Options'))
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
|
|
@ -604,76 +604,86 @@ def test_add_payment_backend(app, admin_user):
|
|||
resp = app.get('/manage/lingo/paymentbackends/add/', status=200)
|
||||
assert '/manage/lingo/paymentbackends/' in resp.text
|
||||
|
||||
resp.forms[0]['label'] = 'Test'
|
||||
resp.forms[0]['slug'] = 'test-add'
|
||||
resp.forms[0]['service'] = 'dummy'
|
||||
resp.forms[0]['service_options'] = '{"siret": "1234"}'
|
||||
resp.form['label'] = 'Test'
|
||||
resp.form['slug'] = 'test-add'
|
||||
resp.form['service'] = 'dummy'
|
||||
resp = resp.forms[0].submit()
|
||||
|
||||
assert PaymentBackend.objects.count() == 1
|
||||
payment_backend = PaymentBackend.objects.get(slug='test-add')
|
||||
assert payment_backend.label == 'Test'
|
||||
assert payment_backend.service_options == {'siret': '1234'}
|
||||
assert payment_backend.service_options == {}
|
||||
|
||||
assert resp.location.endswith('/manage/lingo/paymentbackends/')
|
||||
|
||||
|
||||
def test_add_payment_backend_validate_options(app, admin_user):
|
||||
app = login(app)
|
||||
resp = app.get('/manage/lingo/paymentbackends/add/', status=200)
|
||||
assert '/manage/lingo/paymentbackends/' in resp.text
|
||||
|
||||
resp.forms[0]['label'] = 'Test'
|
||||
resp.forms[0]['slug'] = 'test-add'
|
||||
resp.forms[0]['service'] = 'dummy'
|
||||
resp.forms[0]['service_options'] = 'xx'
|
||||
resp2 = resp.forms[0].submit()
|
||||
assert not PaymentBackend.objects.count()
|
||||
|
||||
resp.forms[0]['service_options'] = '"xx"'
|
||||
resp2 = resp.forms[0].submit()
|
||||
assert not PaymentBackend.objects.count()
|
||||
|
||||
resp.forms[0]['service_options'] = '[1]'
|
||||
resp2 = resp.forms[0].submit()
|
||||
assert not PaymentBackend.objects.count()
|
||||
|
||||
resp.forms[0]['service_options'] = '{"a": 1}'
|
||||
resp2 = resp.forms[0].submit()
|
||||
assert PaymentBackend.objects.count()
|
||||
assert PaymentBackend.objects.get().service_options == {'a': 1}
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_jsonfield_null_bug(app, admin_user):
|
||||
app = login(app)
|
||||
resp = app.get('/manage/lingo/paymentbackends/add/', status=200)
|
||||
assert '/manage/lingo/paymentbackends/' in resp.text
|
||||
|
||||
resp.forms[0]['label'] = 'Test'
|
||||
resp.forms[0]['slug'] = 'test-add'
|
||||
resp.forms[0]['service'] = 'dummy'
|
||||
resp.forms[0]['service_options'] = 'null'
|
||||
resp2 = resp.forms[0].submit()
|
||||
assert PaymentBackend.objects.count()
|
||||
assert PaymentBackend.objects.get().service_options == {'a': 1}
|
||||
backend = PaymentBackend.objects.first()
|
||||
assert resp.location.endswith('/manage/lingo/paymentbackends/%s/edit' % backend.pk)
|
||||
resp = resp.follow()
|
||||
assert 'fill additional backend parameters' in resp.text
|
||||
|
||||
|
||||
def test_edit_payment_backend(app, admin_user):
|
||||
payment_backend = PaymentBackend.objects.create(label='label1', slug='slug1')
|
||||
payment_backend = PaymentBackend.objects.create(label='label1', slug='slug1', service='dummy')
|
||||
app = login(app)
|
||||
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk, status=200)
|
||||
assert '/manage/lingo/paymentbackends/' in resp.text
|
||||
|
||||
resp.forms[0]['label'] = 'label1-modified'
|
||||
resp.forms[0]['slug'] = 'slug1'
|
||||
resp.forms[0]['service'] = 'dummy'
|
||||
resp.forms[0]['service_options'] = '{"siret": "1234"}'
|
||||
resp = resp.forms[0].submit()
|
||||
# service cannot be changed
|
||||
assert 'disabled' in resp.form['service'].attrs
|
||||
|
||||
# deprecated parameters are not shown
|
||||
assert not 'next_url' in resp.form.fields
|
||||
assert not 'direct_notification_url' in resp.form.fields
|
||||
|
||||
# default values become initial values
|
||||
assert resp.form['siret']._value == '1234'
|
||||
|
||||
resp.form['label'] = 'label1-modified'
|
||||
resp.form['siret'] = '12345'
|
||||
resp.form['consider_all_response_signed'] = True
|
||||
resp.form['number'] = 12
|
||||
resp.form['choice'] = 'b'
|
||||
resp.form['choices'] = ['a', 'b']
|
||||
resp = resp.form.submit()
|
||||
|
||||
assert resp.location.endswith('/manage/lingo/paymentbackends/')
|
||||
payment_backend = PaymentBackend.objects.get(slug='slug1')
|
||||
assert payment_backend.label == 'label1-modified'
|
||||
assert payment_backend.service_options['siret'] == '12345'
|
||||
assert payment_backend.service_options['consider_all_response_signed'] == True
|
||||
assert payment_backend.service_options['number'] == 12
|
||||
assert payment_backend.service_options['choice'] == 'b'
|
||||
assert payment_backend.service_options['choices'] == ['a', 'b']
|
||||
|
||||
|
||||
def test_edit_payment_backend_validation(app, admin_user):
|
||||
payment_backend = PaymentBackend.objects.create(label='label1', slug='slug1', service='dummy')
|
||||
app = login(app)
|
||||
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
|
||||
|
||||
# required field
|
||||
resp.form['siret'] = ''
|
||||
resp = resp.form.submit()
|
||||
assert 'This field is required.' in resp.text
|
||||
assert resp.form['siret']._value == None
|
||||
|
||||
# validation function
|
||||
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
|
||||
resp.form['siret'] = 'a'
|
||||
resp = resp.form.submit()
|
||||
assert 'must be a number' in resp.text
|
||||
assert resp.form['siret']._value == 'a'
|
||||
|
||||
# wrong type
|
||||
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
|
||||
resp.form['number'] = 'a'
|
||||
resp = resp.form.submit()
|
||||
assert 'Enter a whole number.' in resp.text
|
||||
assert resp.form['number']._value == 'a'
|
||||
|
||||
# not in choices
|
||||
resp = app.get('/manage/lingo/paymentbackends/%s/edit' % payment_backend.pk)
|
||||
resp.form['choice'].force_value('c')
|
||||
resp = resp.form.submit()
|
||||
assert 'c is not one of the available choices' in resp.text
|
||||
|
||||
|
||||
def test_use_old_service_options_safely(app, admin_user):
|
||||
|
|
Loading…
Reference in New Issue