summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Cazenave <ecazenave@entrouvert.com>2019-05-14 14:38:32 (GMT)
committerEmmanuel Cazenave <ecazenave@entrouvert.com>2019-05-16 15:53:49 (GMT)
commit23f13059d00a761af113a09b92d0031f9648cddb (patch)
treee27a66fa9f739be1ae2c94734605a8faeb7bf9a7
parent2eaeeb367028e9c7ce92fec76ce756a9f6212df8 (diff)
downloadcombo-23f13059d00a761af113a09b92d0031f9648cddb.zip
combo-23f13059d00a761af113a09b92d0031f9648cddb.tar.gz
combo-23f13059d00a761af113a09b92d0031f9648cddb.tar.bz2
lingo: start transaction options (#32967)
Based on the backend capabilities.
-rw-r--r--combo/apps/lingo/forms.py69
-rw-r--r--combo/apps/lingo/manager_views.py14
-rw-r--r--combo/apps/lingo/migrations/0037_regie_transaction_options.py21
-rw-r--r--combo/apps/lingo/models.py4
-rw-r--r--combo/apps/lingo/views.py2
-rw-r--r--tests/test_lingo_manager.py57
-rw-r--r--tests/test_lingo_payment.py26
7 files changed, 180 insertions, 13 deletions
diff --git a/combo/apps/lingo/forms.py b/combo/apps/lingo/forms.py
new file mode 100644
index 0000000..f19573a
--- /dev/null
+++ b/combo/apps/lingo/forms.py
@@ -0,0 +1,69 @@
+# lingo - basket and payment system
+# Copyright (C) 2019 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from django import forms
+
+from .models import Regie
+
+
+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]
+ else:
+ raise NotImplementedError()
+ return fields, initial
+
+
+def compute_json_field(parameters, cleaned_data):
+ json_field = {}
+ for param in parameters:
+ param_name = param['name']
+ if param_name in cleaned_data:
+ json_field[param_name] = cleaned_data[param_name]
+ return json_field
+
+
+class RegieForm(forms.ModelForm):
+
+ class Meta:
+ model = Regie
+ fields = ['label', 'slug', 'description', 'payment_backend', 'is_default',
+ 'webservice_url', 'extra_fees_ws_url', 'payment_min_amount', 'text_on_success']
+
+ def __init__(self, *args, **kwargs):
+ super(RegieForm, self).__init__(*args, **kwargs)
+ fields, initial = create_form_fields(
+ self.instance.payment_backend.get_payment().get_parameters(scope='transaction'),
+ self.instance.transaction_options
+ )
+ self.fields.update(fields)
+ self.initial.update(initial)
+
+ def save(self):
+ instance = super(RegieForm, self).save()
+ instance.transaction_options = compute_json_field(
+ self.instance.payment_backend.get_payment().get_parameters(scope='transaction'),
+ self.cleaned_data
+ )
+ instance.save()
+ return instance
diff --git a/combo/apps/lingo/manager_views.py b/combo/apps/lingo/manager_views.py
index ed2afb4..d9a8e01 100644
--- a/combo/apps/lingo/manager_views.py
+++ b/combo/apps/lingo/manager_views.py
@@ -27,31 +27,23 @@ from django.http import HttpResponse
import eopayment
+from .forms import RegieForm
from .models import PaymentBackend, Regie, Transaction
-REGIE_FIELDS = ['label', 'slug', 'description', 'payment_backend', 'is_default', 'webservice_url',
- 'extra_fees_ws_url', 'payment_min_amount', 'text_on_success']
-
-
class RegieListView(ListView):
model = Regie
class RegieCreateView(CreateView):
model = Regie
- fields = REGIE_FIELDS
+ fields = ['label', 'slug', 'description', 'payment_backend']
success_url = reverse_lazy('lingo-manager-regie-list')
- def get_initial(self):
- if self.model.objects.all().count() == 0:
- return {'is_default': True}
- return {}
-
class RegieUpdateView(UpdateView):
model = Regie
- fields = REGIE_FIELDS
+ form_class = RegieForm
success_url = reverse_lazy('lingo-manager-regie-list')
diff --git a/combo/apps/lingo/migrations/0037_regie_transaction_options.py b/combo/apps/lingo/migrations/0037_regie_transaction_options.py
new file mode 100644
index 0000000..1633d1b
--- /dev/null
+++ b/combo/apps/lingo/migrations/0037_regie_transaction_options.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2019-05-14 12:02
+from __future__ import unicode_literals
+
+from django.db import migrations
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('lingo', '0036_auto_20190426_1202'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='regie',
+ name='transaction_options',
+ field=jsonfield.fields.JSONField(blank=True, default=dict, verbose_name='Transaction Options'),
+ ),
+ ]
diff --git a/combo/apps/lingo/models.py b/combo/apps/lingo/models.py
index a9c700d..f4be4fc 100644
--- a/combo/apps/lingo/models.py
+++ b/combo/apps/lingo/models.py
@@ -98,6 +98,9 @@ class PaymentBackend(models.Model):
def __str__(self):
return self.label
+ def get_payment(self):
+ return eopayment.Payment(self.service, self.service_options)
+
@python_2_unicode_compatible
class Regie(models.Model):
@@ -117,6 +120,7 @@ class Regie(models.Model):
verbose_name=_('Custom text displayed on success'),
blank=True, null=True)
payment_backend = models.ForeignKey(PaymentBackend, on_delete=models.CASCADE)
+ transaction_options = JSONField(blank=True, verbose_name=_('Transaction Options'))
def is_remote(self):
return self.webservice_url != ''
diff --git a/combo/apps/lingo/views.py b/combo/apps/lingo/views.py
index cb4f089..4150d79 100644
--- a/combo/apps/lingo/views.py
+++ b/combo/apps/lingo/views.py
@@ -339,6 +339,8 @@ class PayMixin(object):
capture_date = items[0].capture_date
if capture_date:
kwargs['capture_date'] = capture_date
+ if regie.transaction_options:
+ kwargs.update(regie.transaction_options)
(order_id, kind, data) = payment.request(total_amount, **kwargs)
logger = logging.getLogger(__name__)
logger.info(u'emitted payment request with id %s', smart_text(order_id), extra={
diff --git a/tests/test_lingo_manager.py b/tests/test_lingo_manager.py
index 44c1f17..b226803 100644
--- a/tests/test_lingo_manager.py
+++ b/tests/test_lingo_manager.py
@@ -47,7 +47,6 @@ def test_add_regie(app, admin_user, payment_backend):
resp.forms[0]['slug'] = 'test'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend.pk
- assert resp.form['is_default'].checked is True
resp = resp.forms[0].submit()
assert resp.location.endswith('/manage/lingo/regies/')
assert Regie.objects.count() == 1
@@ -68,6 +67,61 @@ def test_edit_regie(app, admin_user, payment_backend):
regie = Regie.objects.all()[0]
assert regie.description == 'other description'
+
+def test_edit_regie_dynamic_backend_fields(app, admin_user):
+ payment_backend = PaymentBackend.objects.create(
+ label='test1', slug='test1', service='systempayv2', service_options={'siret': '1234'})
+ regie = Regie.objects.create(
+ label='test-regie', slug='test-regie', payment_backend=payment_backend)
+ assert regie.transaction_options == {}
+
+ app = login(app)
+ resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
+ resp.forms[0]['label'] = 'test-regie'
+ resp.forms[0]['slug'] = 'test-regie'
+ resp.forms[0]['description'] = 'description'
+ resp.forms[0]['payment_backend'] = payment_backend.pk
+
+ assert resp.forms[0]['manual_validation'].checked is False
+ resp.forms[0]['manual_validation'] = True
+ resp = resp.forms[0].submit()
+
+ assert Regie.objects.count() == 1
+
+ regie = Regie.objects.get(slug='test-regie')
+ assert regie.transaction_options['manual_validation']
+
+ # No dynamic fields if no backend capabilities
+ payment_backend_dummy = PaymentBackend.objects.create(
+ label='test1', slug='test-dummy', service='dummy', service_options={'siret': '1234'})
+ regie_dummy = Regie.objects.create(
+ label='test-regie-2', slug='test-regie-2', payment_backend=payment_backend_dummy)
+
+ resp = app.get('/manage/lingo/regies/%s/edit' % regie_dummy.pk, status=200)
+ assert 'manual_validation' not in resp.forms[0].fields
+ resp.forms[0]['label'] = 'Test'
+ resp.forms[0]['slug'] = 'test-regie-2'
+ resp.forms[0]['description'] = 'description'
+ resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
+ resp = resp.forms[0].submit()
+
+ regie_dummy = Regie.objects.get(slug='test-regie-2')
+ assert regie_dummy.transaction_options == {}
+
+ # Change backend
+ resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
+ resp.forms[0]['label'] = 'test-regie'
+ resp.forms[0]['slug'] = 'test-regie'
+ resp.forms[0]['description'] = 'description'
+ resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
+
+ assert resp.forms[0]['manual_validation'].checked is True
+ resp.forms[0]['manual_validation'] = True
+ resp = resp.forms[0].submit()
+
+ regie = Regie.objects.get(slug='test-regie')
+ assert regie.transaction_options == {}
+
def test_delete_regie(app, admin_user, payment_backend):
test_add_regie(app, admin_user, payment_backend)
app = login(app)
@@ -88,7 +142,6 @@ def test_add_second_regie(app, admin_user, payment_backend):
resp.forms[0]['slug'] = 'test2'
resp.forms[0]['description'] = 'description'
resp.forms[0]['payment_backend'] = payment_backend.pk
- assert resp.form['is_default'].checked is False
resp = resp.forms[0].submit()
assert resp.location.endswith('/manage/lingo/regies/')
diff --git a/tests/test_lingo_payment.py b/tests/test_lingo_payment.py
index 45666ce..902578b 100644
--- a/tests/test_lingo_payment.py
+++ b/tests/test_lingo_payment.py
@@ -136,6 +136,32 @@ def test_payment_min_amount(app, basket_page, regie, user):
assert resp.status_code == 302
+def test_transaction_manual_validation(app, basket_page, user, monkeypatch):
+ pb = PaymentBackend.objects.create(
+ label='test1', slug='test1', service='payzen',
+ service_options={'vads_site_id': '12345678', 'secret_test': 'plkoBdfcx987dhft6'}
+
+ )
+ regie = Regie.objects.create(
+ label='Test', slug='test', description='test', payment_backend=pb,
+ transaction_options={'manual_validation': True})
+ BasketItem.objects.create(
+ user=user, regie=regie, subject='item1', amount='1.5', source_url='/item/1')
+
+ class MockPayment(object):
+ request = mock.Mock(return_value=(9876, 3, {}))
+
+ def get_eopayment_object(*args, **kwargs):
+ return MockPayment
+
+ import combo.apps.lingo.views
+ monkeypatch.setattr(combo.apps.lingo.views, 'get_eopayment_object', get_eopayment_object)
+
+ resp = login(app).get('/test_basket_cell/')
+ resp = resp.form.submit()
+ assert MockPayment.request.call_args.kwargs['manual_validation'] is True
+
+
@pytest.mark.parametrize('with_payment_backend', [False, True])
def test_successfull_items_payment(app, basket_page, regie, user, with_payment_backend):
items = {'item1': {'amount': '10.5', 'source_url': 'http://example.org/item/1'},