From cc4815c82c1365f4a2f11413956740c2c20d87ee Mon Sep 17 00:00:00 2001
From: Hugo LEVY-FALK
Date: Mon, 2 Jul 2018 21:13:13 +0200
Subject: [PATCH] =?UTF-8?q?Permet=20la=20cr=C3=A9ation=20et=20l'=C3=A9diti?=
=?UTF-8?q?on=20de=20moyen=20de=20paiement=20personnalis=C3=A9s.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cotisations/forms.py | 7 +--
.../0032_chequepayment_comnpaypayment.py | 15 +++--
.../migrations/0033_auto_20180628_2157.py | 20 -------
cotisations/payment_methods/__init__.py | 16 +++++
.../payment_methods/cheque/__init__.py | 2 +-
cotisations/payment_methods/cheque/forms.py | 10 ++--
cotisations/payment_methods/cheque/models.py | 15 +++--
.../payment_methods/comnpay/__init__.py | 2 +-
cotisations/payment_methods/comnpay/models.py | 15 +++--
cotisations/payment_methods/forms.py | 59 +++++++++++++++++++
cotisations/payment_methods/mixins.py | 21 +++++++
.../templates/cotisations/facture.html | 37 +++++++++++-
cotisations/views.py | 41 +++++++++----
13 files changed, 198 insertions(+), 62 deletions(-)
delete mode 100644 cotisations/migrations/0033_auto_20180628_2157.py
create mode 100644 cotisations/payment_methods/forms.py
create mode 100644 cotisations/payment_methods/mixins.py
diff --git a/cotisations/forms.py b/cotisations/forms.py
index a5db7b92..0ee4c143 100644
--- a/cotisations/forms.py
+++ b/cotisations/forms.py
@@ -242,17 +242,12 @@ class PaiementForm(FormRevMixin, ModelForm):
class Meta:
model = Paiement
# TODO : change moyen to method and type_paiement to payment_type
- fields = ['moyen', 'type_paiement', 'allow_self_subscription']
+ fields = ['moyen', 'allow_self_subscription']
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['moyen'].label = _("Payment method name")
- self.fields['type_paiement'].label = _("Payment type")
- self.fields['type_paiement'].help_text = \
- _("The payement type is used for specific behaviour.\
- The \"cheque\" type means a cheque number and a bank name\
- may be added when using this payment method.")
# TODO : change paiement to payment
diff --git a/cotisations/migrations/0032_chequepayment_comnpaypayment.py b/cotisations/migrations/0032_chequepayment_comnpaypayment.py
index 76fc4ea0..d2c67308 100644
--- a/cotisations/migrations/0032_chequepayment_comnpaypayment.py
+++ b/cotisations/migrations/0032_chequepayment_comnpaypayment.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.10.7 on 2018-06-28 17:28
+# Generated by Django 1.10.7 on 2018-07-02 18:56
from __future__ import unicode_literals
import cotisations.payment_methods.comnpay.aes_field
+import cotisations.payment_methods.models
from django.db import migrations, models
import django.db.models.deletion
@@ -29,6 +30,8 @@ def add_comnpay(apps, schema_editor):
comnpay.payment_pass = options.payment_pass
comnpay.payment = payment
comnpay.save()
+ payment.moyen = "ComnPay"
+ payment.save()
class Migration(migrations.Migration):
@@ -42,18 +45,20 @@ class Migration(migrations.Migration):
name='ChequePayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')),
+ ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')),
],
+ bases=(cotisations.payment_methods.models.PaymentMethodMixin, models.Model),
),
migrations.CreateModel(
name='ComnpayPayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('payment_user', models.CharField(blank=True, default='', max_length=255)),
+ ('payment_credential', models.CharField(blank=True, default='', max_length=255)),
('payment_pass', cotisations.payment_methods.comnpay.aes_field.AESEncryptedField(blank=True, max_length=255, null=True)),
- ('payment', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')),
+ ('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')),
],
+ bases=(cotisations.payment_methods.models.PaymentMethodMixin, models.Model),
),
- migrations.RunPython(add_cheque),
migrations.RunPython(add_comnpay),
+ migrations.RunPython(add_cheque),
]
diff --git a/cotisations/migrations/0033_auto_20180628_2157.py b/cotisations/migrations/0033_auto_20180628_2157.py
deleted file mode 100644
index 51509105..00000000
--- a/cotisations/migrations/0033_auto_20180628_2157.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.7 on 2018-06-28 19:57
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('cotisations', '0032_chequepayment_comnpaypayment'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='comnpaypayment',
- name='payment_id',
- field=models.CharField(blank=True, default='', max_length=255),
- ),
- ]
diff --git a/cotisations/payment_methods/__init__.py b/cotisations/payment_methods/__init__.py
index cc21f42b..36904411 100644
--- a/cotisations/payment_methods/__init__.py
+++ b/cotisations/payment_methods/__init__.py
@@ -1,4 +1,5 @@
from django.conf.urls import include, url
+from django.utils.translation import ugettext_lazy as _l
from . import comnpay, cheque
@@ -7,3 +8,18 @@ urlpatterns = [
url(r'^comnpay/', include(comnpay.urls, namespace='comnpay')),
url(r'^cheque/', include(cheque.urls, namespace='cheque')),
]
+
+PAYMENT_METHODS = [
+ comnpay,
+ cheque,
+]
+
+
+def find_payment_method(payment):
+ for method in PAYMENT_METHODS:
+ try:
+ o = method.PaymentMethod.objects.get(payment=payment)
+ return o
+ except method.PaymentMethod.DoesNotExist:
+ pass
+ return None
diff --git a/cotisations/payment_methods/cheque/__init__.py b/cotisations/payment_methods/cheque/__init__.py
index 6610cdd8..a6fef640 100644
--- a/cotisations/payment_methods/cheque/__init__.py
+++ b/cotisations/payment_methods/cheque/__init__.py
@@ -4,4 +4,4 @@ This module contains a method to pay online using cheque.
from . import models, urls, views
NAME = "CHEQUE"
-Payment = models.ChequePayment
+PaymentMethod = models.ChequePayment
diff --git a/cotisations/payment_methods/cheque/forms.py b/cotisations/payment_methods/cheque/forms.py
index 892ba190..ae816a4b 100644
--- a/cotisations/payment_methods/cheque/forms.py
+++ b/cotisations/payment_methods/cheque/forms.py
@@ -1,10 +1,12 @@
from django import forms
from django.utils.translation import ugettext_lazy as _l
-from cotisations.models import Banque as Bank
+from re2o.mixins import FormRevMixin
+from cotisations.models import Facture as Invoice
-class ChequeForm(forms.Form):
+class InvoiceForm(FormRevMixin, forms.ModelForm):
"""A simple form to get the bank a the cheque number."""
- bank = forms.ModelChoiceField(Bank.objects.all(), label=_l("Bank"))
- number = forms.CharField(label=_l("Cheque number"))
+ class Meta:
+ model = Invoice
+ fields = ['banque', 'cheque']
diff --git a/cotisations/payment_methods/cheque/models.py b/cotisations/payment_methods/cheque/models.py
index 336f025e..29c86a41 100644
--- a/cotisations/payment_methods/cheque/models.py
+++ b/cotisations/payment_methods/cheque/models.py
@@ -2,16 +2,19 @@ from django.db import models
from django.shortcuts import redirect
from django.urls import reverse
-from cotisations.models import Paiement as BasePayment
+from cotisations.models import Paiement
+from cotisations.payment_methods.mixins import PaymentMethodMixin
-class ChequePayment(models.Model):
+class ChequePayment(PaymentMethodMixin, models.Model):
"""
- The model allowing you to pay with a cheque. It redefines post_payment
- method. See `cotisations.models.Paiement for further details.
+ The model allowing you to pay with a cheque.
"""
- payment = models.OneToOneField(BasePayment, related_name='payment_method')
-
+ payment = models.OneToOneField(
+ Paiement,
+ related_name='payment_method',
+ editable=False
+ )
def end_payment(self, invoice, request):
invoice.valid = False
invoice.save()
diff --git a/cotisations/payment_methods/comnpay/__init__.py b/cotisations/payment_methods/comnpay/__init__.py
index d0289364..58433c80 100644
--- a/cotisations/payment_methods/comnpay/__init__.py
+++ b/cotisations/payment_methods/comnpay/__init__.py
@@ -3,4 +3,4 @@ This module contains a method to pay online using comnpay.
"""
from . import models, urls, views
NAME = "COMNPAY"
-Payment = models.ComnpayPayment
+PaymentMethod = models.ComnpayPayment
diff --git a/cotisations/payment_methods/comnpay/models.py b/cotisations/payment_methods/comnpay/models.py
index 4fc509af..68f883d4 100644
--- a/cotisations/payment_methods/comnpay/models.py
+++ b/cotisations/payment_methods/comnpay/models.py
@@ -1,19 +1,22 @@
from django.db import models
from django.shortcuts import render
-from cotisations.models import Paiement as BasePayment
+from cotisations.models import Paiement
+from cotisations.payment_methods.mixins import PaymentMethodMixin
from .aes_field import AESEncryptedField
from .views import comnpay
-class ComnpayPayment(models.Model):
+class ComnpayPayment(PaymentMethodMixin, models.Model):
"""
- The model allowing you to pay with COMNPAY. It redefines post_payment
- method. See `cotisations.models.Paiement for further details.
+ The model allowing you to pay with COMNPAY.
"""
- payment = models.OneToOneField(BasePayment, related_name='payment_method')
-
+ payment = models.OneToOneField(
+ Paiement,
+ related_name='payment_method',
+ editable=False
+ )
payment_credential = models.CharField(
max_length=255,
default='',
diff --git a/cotisations/payment_methods/forms.py b/cotisations/payment_methods/forms.py
new file mode 100644
index 00000000..2acc80f0
--- /dev/null
+++ b/cotisations/payment_methods/forms.py
@@ -0,0 +1,59 @@
+from django import forms
+from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext_lazy as _l
+
+from . import PAYMENT_METHODS, find_payment_method
+
+
+def payment_method_factory(payment, *args, **kwargs):
+ payment_method = kwargs.pop('instance', find_payment_method(payment))
+ if payment_method is not None:
+ return forms.modelform_factory(type(payment_method), fields='__all__')(
+ *args,
+ instance=payment_method,
+ **kwargs
+ )
+ return PaymentMethodForm(payment_method, *args, **kwargs)
+
+
+class PaymentMethodForm(forms.Form):
+ """A special form which allows you to add a payment method to a `Payment`
+ objects if it hasn't one yet, or to edit the existing payment method.
+
+ To do so it replaces itself with a `modelform_factory`.
+ """
+
+ payment_method = forms.ChoiceField(
+ label=_l("Special payment method"),
+ required=False
+ )
+
+ def __init__(self, payment_method, *args, **kwargs):
+ super(PaymentMethodForm, self).__init__(*args, **kwargs)
+ if payment_method is None:
+ prefix = kwargs.get('prefix', None)
+ self.fields['payment_method'].choices = [(i,p.NAME) for (i,p) in enumerate(PAYMENT_METHODS)]
+ self.fields['payment_method'].choices.insert(0, ('', _l('no')))
+ self.fields['payment_method'].widget.attrs = {
+ 'id': 'paymentMethodSelect'
+ }
+ self.templates = [
+ forms.modelform_factory(p.PaymentMethod, fields='__all__')(prefix=prefix)
+ for p in PAYMENT_METHODS
+ ]
+ else:
+ self.fields = {}
+
+ def save(self, *args, payment=None, **kwargs):
+ commit = kwargs.pop('commit', True)
+ choice = self.cleaned_data['payment_method']
+ if choice=='':
+ return
+ choice = int(choice)
+ model = PAYMENT_METHODS[choice].PaymentMethod
+ form = forms.modelform_factory(model, fields='__all__')(self.data, prefix=self.prefix)
+ payment_method = form.save(commit=False)
+ payment_method.payment = payment
+ if commit:
+ payment_method.save()
+ return payment_method
diff --git a/cotisations/payment_methods/mixins.py b/cotisations/payment_methods/mixins.py
new file mode 100644
index 00000000..62ebbf32
--- /dev/null
+++ b/cotisations/payment_methods/mixins.py
@@ -0,0 +1,21 @@
+from django.db import models
+
+from cotisations.models import Paiement
+
+
+class PaymentMethodMixin:
+ """The base class for payment models. They should inherit from this."""
+ payment = models.OneToOneField(
+ Paiement,
+ related_name='payment_method',
+ editable=False
+ )
+
+ def end_payment(self, invoice, request):
+ """Redefine this method in order to get a different ending to the
+ payment session if you whish.
+
+ Must return a HttpResponse-like object.
+ """
+ return self.payment.end_payment(
+ invoice, request, use_payment_method=False)
diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html
index dc8648ac..082759c4 100644
--- a/cotisations/templates/cotisations/facture.html
+++ b/cotisations/templates/cotisations/facture.html
@@ -57,16 +57,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
{% bootstrap_form factureform %}
+ {% if payment_method %}
+ {% bootstrap_form payment_method %}
+
+ {% endif %}
{% bootstrap_button action_name button_type='submit' icon='star' %}
-{% if articlesformset %}
+{% if articlesformset or payment_method%}
{% endif %}
diff --git a/cotisations/views.py b/cotisations/views.py
index ea137379..280a3aa9 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -74,6 +74,7 @@ from .forms import (
RechargeForm
)
from .tex import render_invoice
+from .payment_methods.forms import payment_method_factory
@login_required
@@ -473,9 +474,15 @@ def add_paiement(request):
"""
View used to add a payment method.
"""
- payment = PaiementForm(request.POST or None)
- if payment.is_valid():
- payment.save()
+ payment = PaiementForm(request.POST or None, prefix='payment')
+ payment_method = payment_method_factory(
+ payment.instance,
+ request.POST or None,
+ prefix='payment_method'
+ )
+ if payment.is_valid() and payment_method.is_valid():
+ payment = payment.save()
+ payment_method.save(payment=payment)
messages.success(
request,
_("The payment method has been successfully created.")
@@ -483,6 +490,7 @@ def add_paiement(request):
return redirect(reverse('cotisations:index-paiement'))
return form({
'factureform': payment,
+ 'payment_method': payment_method,
'action_name': _("Add")
}, 'cotisations/facture.html', request)
@@ -494,17 +502,28 @@ def edit_paiement(request, paiement_instance, **_kwargs):
"""
View used to edit a payment method.
"""
- payment = PaiementForm(request.POST or None, instance=paiement_instance)
- if payment.is_valid():
- if payment.changed_data:
- payment.save()
- messages.success(
- request,
- _("The payement method has been successfully edited.")
- )
+ payment = PaiementForm(
+ request.POST or None,
+ instance=paiement_instance,
+ prefix="payment"
+ )
+ payment_method = payment_method_factory(
+ paiement_instance,
+ request.POST or None,
+ prefix='payment_method'
+ )
+
+ if payment.is_valid() and payment_method.is_valid():
+ payment.save()
+ payment_method.save()
+ messages.success(
+ request,
+ _("The payement method has been successfully edited.")
+ )
return redirect(reverse('cotisations:index-paiement'))
return form({
'factureform': payment,
+ 'payment_method': payment_method,
'action_name': _("Edit")
}, 'cotisations/facture.html', request)