mirror of https://gitlab.federez.net/re2o/re2o
Browse Source
Refactorisation des moyens de paiement See merge request federez/re2o!174fix_front_profil
45 changed files with 1759 additions and 1115 deletions
@ -0,0 +1,132 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-07-02 18:56 |
|||
from __future__ import unicode_literals |
|||
|
|||
import re2o.aes_field |
|||
import cotisations.payment_methods.mixins |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
def add_cheque(apps, schema_editor): |
|||
ChequePayment = apps.get_model('cotisations', 'ChequePayment') |
|||
Payment = apps.get_model('cotisations', 'Paiement') |
|||
for p in Payment.objects.filter(type_paiement=1): |
|||
cheque = ChequePayment() |
|||
cheque.payment = p |
|||
cheque.save() |
|||
|
|||
|
|||
def add_comnpay(apps, schema_editor): |
|||
ComnpayPayment = apps.get_model('cotisations', 'ComnpayPayment') |
|||
Payment = apps.get_model('cotisations', 'Paiement') |
|||
AssoOption = apps.get_model('preferences', 'AssoOption') |
|||
options, _created = AssoOption.objects.get_or_create() |
|||
try: |
|||
payment = Payment.objects.get( |
|||
moyen='Rechargement en ligne' |
|||
) |
|||
except Payment.DoesNotExist: |
|||
return |
|||
comnpay = ComnpayPayment() |
|||
comnpay.payment_user = options.payment_id |
|||
comnpay.payment = payment |
|||
comnpay.save() |
|||
payment.moyen = "ComnPay" |
|||
|
|||
payment.save() |
|||
|
|||
|
|||
def add_solde(apps, schema_editor): |
|||
OptionalUser = apps.get_model('preferences', 'OptionalUser') |
|||
options, _created = OptionalUser.objects.get_or_create() |
|||
|
|||
Payment = apps.get_model('cotisations', 'Paiement') |
|||
BalancePayment = apps.get_model('cotisations', 'BalancePayment') |
|||
|
|||
try: |
|||
solde = Payment.objects.get(moyen="solde") |
|||
except Payment.DoesNotExist: |
|||
return |
|||
balance = BalancePayment() |
|||
balance.payment = solde |
|||
balance.minimum_balance = options.solde_negatif |
|||
balance.maximum_balance = options.max_solde |
|||
solde.is_balance = True |
|||
balance.save() |
|||
solde.save() |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0044_remove_payment_pass'), |
|||
('cotisations', '0029_auto_20180414_2056'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='paiement', |
|||
options={'permissions': (('view_paiement', "Can see a payement's details"), ('use_every_payment', 'Can use every payement')), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'}, |
|||
), |
|||
migrations.AlterModelOptions( |
|||
name='article', |
|||
options={'permissions': (('view_article', "Can see an article's details"), ('buy_every_article', 'Can buy every_article')), 'verbose_name': 'Article', 'verbose_name_plural': 'Articles'}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='paiement', |
|||
name='available_for_everyone', |
|||
field=models.BooleanField(default=False, verbose_name='Is available for every user'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='paiement', |
|||
name='is_balance', |
|||
field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', verbose_name='Is user balance', validators=[cotisations.models.check_no_balance]), |
|||
), |
|||
migrations.AddField( |
|||
model_name='article', |
|||
name='available_for_everyone', |
|||
field=models.BooleanField(default=False, verbose_name='Is available for every user'), |
|||
), |
|||
migrations.CreateModel( |
|||
name='ChequePayment', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), |
|||
], |
|||
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), |
|||
options={'verbose_name': 'Cheque'}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='ComnpayPayment', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('payment_credential', models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAD Number')), |
|||
('payment_pass', re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay Secret Key')), |
|||
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), |
|||
('minimum_payment', models.DecimalField(decimal_places=2, default=1, help_text='The minimal amount of money you have to use when paying with ComNpay', max_digits=5, verbose_name='Minimum payment')), |
|||
], |
|||
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), |
|||
options={'verbose_name': 'ComNpay'}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='BalancePayment', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('minimum_balance', models.DecimalField(decimal_places=2, default=0, help_text='The minimal amount of money allowed for the balance at the end of a payment. You can specify negative amount.', max_digits=5, verbose_name='Minimum balance')), |
|||
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), |
|||
('maximum_balance', models.DecimalField(decimal_places=2, default=50, help_text='The maximal amount of money allowed for the balance.', max_digits=5, verbose_name='Maximum balance', null=True, blank=True)), |
|||
('credit_balance_allowed', models.BooleanField(default=False, verbose_name='Allow user to credit their balance')), |
|||
], |
|||
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), |
|||
options={'verbose_name': 'User Balance'}, |
|||
), |
|||
migrations.RunPython(add_comnpay), |
|||
migrations.RunPython(add_cheque), |
|||
migrations.RunPython(add_solde), |
|||
migrations.RemoveField( |
|||
model_name='paiement', |
|||
name='type_paiement', |
|||
), |
|||
|
|||
] |
|||
@ -0,0 +1,136 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
""" |
|||
# Custom Payment methods |
|||
|
|||
When creating an invoice with a classic payment method, the creation view calls |
|||
the `end_payment` method of the `Payment` object of the invoice. This method |
|||
checks for a payment method associated to the `Payment` and if nothing is |
|||
found, adds a message for payment confirmation and redirects the user towards |
|||
their profil page. This is fine for most of the payment method, but you might |
|||
want to define custom payment methods. As an example for negociating with an |
|||
other server for online payment or updating some fields in your models. |
|||
|
|||
# Defining a custom payment method |
|||
To define a custom payment method, you can add a Python module to |
|||
`cotisations/payment_methods/`. This module should be organized like |
|||
a Django application. |
|||
As an example, if you want to add the payment method `foo`. |
|||
|
|||
## Basic |
|||
|
|||
The first thing to do is to create a `foo` Python module with a `models.py`. |
|||
|
|||
``` |
|||
payment_methods |
|||
├── foo |
|||
│ ├── __init__.py |
|||
│ └── models.py |
|||
├── forms.py |
|||
├── __init__.py |
|||
├── mixins.py |
|||
└── urls.py |
|||
``` |
|||
|
|||
Then, in `models.py` you could add a model like this : |
|||
```python |
|||
from django.db import models |
|||
|
|||
from cotisations.models import Paiement |
|||
from cotisations.payment_methods.mixins import PaymentMethodMixin |
|||
|
|||
|
|||
# The `PaymentMethodMixin` defines the default `end_payment` |
|||
class FooPayment(PaymentMethodMixin, models.Model): |
|||
|
|||
# This field is required, it is used by `Paiement` in order to |
|||
# determine if a payment method is associated to it. |
|||
payment = models.OneToOneField( |
|||
Paiement, |
|||
on_delete=models.CASCADE, |
|||
related_name='payment_method', |
|||
editable=False |
|||
) |
|||
``` |
|||
|
|||
And in `__init__.py` : |
|||
```python |
|||
from . import models |
|||
NAME = "FOO" # Name displayed when you crate a payment type |
|||
PaymentMethod = models.FooPayment # You must define this alias |
|||
``` |
|||
|
|||
Then you just have to register your payment method in |
|||
`payment_methods/__init__.py` in the `PAYMENT_METHODS` list : |
|||
|
|||
``` |
|||
from . import ... # Some existing imports |
|||
from . import foo |
|||
|
|||
PAYMENT_METHODS = [ |
|||
# Some already registered payment methods... |
|||
foo |
|||
] |
|||
``` |
|||
|
|||
And... that's it, you can use your new payment method after running |
|||
`makemigrations` and `migrate`. |
|||
|
|||
But this payment method is not really usefull, since it does noting ! |
|||
|
|||
## A payment method which does something |
|||
|
|||
You have to redefine the `end_payment` method. Here is its prototype : |
|||
|
|||
```python |
|||
def end_payment(self, invoice, request): |
|||
pass |
|||
``` |
|||
|
|||
With `invoice` the invoice being created and `request` the request which |
|||
created it. This method has to return an HttpResponse-like object. |
|||
|
|||
## Additional views |
|||
|
|||
You can add specific urls for your payment method like in any django app. To |
|||
register these urls, modify `payment_methods/urls.py`. |
|||
|
|||
## Alter the `Paiement` object after creation |
|||
|
|||
You can do that by adding a `alter_payment(self, payment)` |
|||
method to your model. |
|||
|
|||
## Validate the creation field |
|||
|
|||
You may want to perform some additionals verifications on the form |
|||
creating the payment. You can do that by adding a `valid_form(self, form)` |
|||
method to your model, where `form` is an instance of |
|||
`cotisations.payment_methods.forms.PaymentMethodForm`. |
|||
""" |
|||
|
|||
|
|||
from . import comnpay, cheque, balance, urls |
|||
|
|||
PAYMENT_METHODS = [ |
|||
comnpay, |
|||
cheque, |
|||
balance, |
|||
] |
|||
@ -0,0 +1,27 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
""" |
|||
This module contains a method to pay online using user balance. |
|||
""" |
|||
from . import models |
|||
NAME = "BALANCE" |
|||
|
|||
PaymentMethod = models.BalancePayment |
|||
@ -0,0 +1,120 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.db import models |
|||
from django.shortcuts import redirect |
|||
from django.urls import reverse |
|||
from django.utils.translation import ugettext as _ |
|||
from django.utils.translation import ugettext_lazy as _l |
|||
from django.contrib import messages |
|||
|
|||
|
|||
from cotisations.models import Paiement |
|||
from cotisations.payment_methods.mixins import PaymentMethodMixin |
|||
|
|||
|
|||
class BalancePayment(PaymentMethodMixin, models.Model): |
|||
""" |
|||
The model allowing you to pay with a cheque. |
|||
""" |
|||
|
|||
class Meta: |
|||
verbose_name = _l("User Balance") |
|||
|
|||
payment = models.OneToOneField( |
|||
Paiement, |
|||
on_delete=models.CASCADE, |
|||
related_name='payment_method', |
|||
editable=False |
|||
) |
|||
minimum_balance = models.DecimalField( |
|||
verbose_name=_l("Minimum balance"), |
|||
help_text=_l("The minimal amount of money allowed for the balance" |
|||
" at the end of a payment. You can specify negative " |
|||
"amount." |
|||
), |
|||
max_digits=5, |
|||
decimal_places=2, |
|||
default=0, |
|||
) |
|||
maximum_balance = models.DecimalField( |
|||
verbose_name=_l("Maximum balance"), |
|||
help_text=_l("The maximal amount of money allowed for the balance."), |
|||
max_digits=5, |
|||
decimal_places=2, |
|||
default=50, |
|||
blank=True, |
|||
null=True, |
|||
) |
|||
credit_balance_allowed = models.BooleanField( |
|||
verbose_name=_l("Allow user to credit their balance"), |
|||
default=False, |
|||
) |
|||
|
|||
def end_payment(self, invoice, request): |
|||
"""Changes the user's balance to pay the invoice. If it is not |
|||
possible, shows an error and invalidates the invoice. |
|||
""" |
|||
user = invoice.user |
|||
total_price = invoice.prix_total() |
|||
if float(user.solde) - float(total_price) < self.minimum_balance: |
|||
invoice.valid = False |
|||
invoice.save() |
|||
messages.error( |
|||
request, |
|||
_("Your balance is too low for this operation.") |
|||
) |
|||
return redirect(reverse( |
|||
'users:profil', |
|||
kwargs={'userid': user.id} |
|||
)) |
|||
return invoice.paiement.end_payment( |
|||
invoice, |
|||
request, |
|||
use_payment_method=False |
|||
) |
|||
|
|||
def valid_form(self, form): |
|||
"""Checks that there is not already a balance payment method.""" |
|||
p = Paiement.objects.filter(is_balance=True) |
|||
if len(p) > 0: |
|||
form.add_error( |
|||
'payment_method', |
|||
_("There is already a payment type for user balance") |
|||
) |
|||
|
|||
def alter_payment(self, payment): |
|||
"""Register the payment as a balance payment.""" |
|||
self.payment.is_balance = True |
|||
|
|||
def check_price(self, price, user, *args, **kwargs): |
|||
"""Checks that the price meets the requirement to be paid with user |
|||
balance. |
|||
""" |
|||
return ( |
|||
float(user.solde) - float(price) >= self.minimum_balance, |
|||
_("Your balance is too low for this operation.") |
|||
) |
|||
|
|||
def can_credit_balance(self, user_request): |
|||
return ( |
|||
len(Paiement.find_allowed_payments(user_request) |
|||
.exclude(is_balance=True)) > 0 |
|||
) and self.credit_balance_allowed |
|||
@ -0,0 +1,27 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
""" |
|||
This module contains a method to pay online using cheque. |
|||
""" |
|||
from . import models, urls, views |
|||
NAME = "CHEQUE" |
|||
|
|||
PaymentMethod = models.ChequePayment |
|||
@ -0,0 +1,31 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django import forms |
|||
|
|||
from re2o.mixins import FormRevMixin |
|||
from cotisations.models import Facture as Invoice |
|||
|
|||
|
|||
class InvoiceForm(FormRevMixin, forms.ModelForm): |
|||
"""A simple form to get the bank a the cheque number.""" |
|||
class Meta: |
|||
model = Invoice |
|||
fields = ['banque', 'cheque'] |
|||
@ -0,0 +1,54 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.db import models |
|||
from django.shortcuts import redirect |
|||
from django.urls import reverse |
|||
from django.utils.translation import ugettext_lazy as _l |
|||
|
|||
from cotisations.models import Paiement |
|||
from cotisations.payment_methods.mixins import PaymentMethodMixin |
|||
|
|||
|
|||
class ChequePayment(PaymentMethodMixin, models.Model): |
|||
""" |
|||
The model allowing you to pay with a cheque. |
|||
""" |
|||
|
|||
class Meta: |
|||
verbose_name = _l("Cheque") |
|||
|
|||
payment = models.OneToOneField( |
|||
Paiement, |
|||
on_delete=models.CASCADE, |
|||
related_name='payment_method', |
|||
editable=False |
|||
) |
|||
|
|||
def end_payment(self, invoice, request): |
|||
"""Invalidates the invoice then redirect the user towards a view asking |
|||
for informations to add to the invoice before validating it. |
|||
""" |
|||
invoice.valid = False |
|||
invoice.save() |
|||
return redirect(reverse( |
|||
'cotisations:cheque:validate', |
|||
kwargs={'invoice_pk': invoice.pk} |
|||
)) |
|||
@ -0,0 +1,30 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.conf.urls import url |
|||
from . import views |
|||
|
|||
urlpatterns = [ |
|||
url( |
|||
r'^validate/(?P<invoice_pk>[0-9]+)$', |
|||
views.cheque, |
|||
name='validate' |
|||
) |
|||
] |
|||
@ -0,0 +1,69 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
"""Payment |
|||
|
|||
Here are defined some views dedicated to cheque payement. |
|||
""" |
|||
|
|||
from django.urls import reverse |
|||
from django.shortcuts import redirect, render, get_object_or_404 |
|||
from django.contrib.auth.decorators import login_required |
|||
from django.contrib import messages |
|||
from django.utils.translation import ugettext as _ |
|||
|
|||
from cotisations.models import Facture as Invoice |
|||
from cotisations.utils import find_payment_method |
|||
|
|||
from .models import ChequePayment |
|||
from .forms import InvoiceForm |
|||
|
|||
|
|||
@login_required |
|||
def cheque(request, invoice_pk): |
|||
"""This view validate an invoice with the data from a cheque.""" |
|||
invoice = get_object_or_404(Invoice, pk=invoice_pk) |
|||
payment_method = find_payment_method(invoice.paiement) |
|||
if invoice.valid or not isinstance(payment_method, ChequePayment): |
|||
messages.error( |
|||
request, |
|||
_("You cannot pay this invoice with a cheque.") |
|||
) |
|||
return redirect(reverse( |
|||
'users:profil', |
|||
kwargs={'userid': request.user.pk} |
|||
)) |
|||
form = InvoiceForm(request.POST or None, instance=invoice) |
|||
if form.is_valid(): |
|||
form.instance.valid = True |
|||
form.save() |
|||
return form.instance.paiement.end_payment( |
|||
form.instance, |
|||
request, |
|||
use_payment_method=False |
|||
) |
|||
return render( |
|||
request, |
|||
'cotisations/payment.html', |
|||
{ |
|||
'form': form, |
|||
'amount': invoice.prix_total() |
|||
} |
|||
) |
|||
@ -0,0 +1,26 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
""" |
|||
This module contains a method to pay online using comnpay. |
|||
""" |
|||
from . import models, urls, views |
|||
NAME = "COMNPAY" |
|||
PaymentMethod = models.ComnpayPayment |
|||
@ -0,0 +1,108 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.db import models |
|||
from django.shortcuts import render |
|||
from django.urls import reverse |
|||
from django.utils.translation import ugettext as _ |
|||
from django.utils.translation import ugettext_lazy as _l |
|||
|
|||
from cotisations.models import Paiement |
|||
from cotisations.payment_methods.mixins import PaymentMethodMixin |
|||
|
|||
from re2o.aes_field import AESEncryptedField |
|||
from .comnpay import Transaction |
|||
|
|||
|
|||
class ComnpayPayment(PaymentMethodMixin, models.Model): |
|||
""" |
|||
The model allowing you to pay with COMNPAY. |
|||
""" |
|||
|
|||
class Meta: |
|||
verbose_name = "ComNpay" |
|||
|
|||
payment = models.OneToOneField( |
|||
Paiement, |
|||
on_delete=models.CASCADE, |
|||
related_name='payment_method', |
|||
editable=False |
|||
) |
|||
payment_credential = models.CharField( |
|||
max_length=255, |
|||
default='', |
|||
blank=True, |
|||
verbose_name=_l("ComNpay VAD Number"), |
|||
) |
|||
payment_pass = AESEncryptedField( |
|||
max_length=255, |
|||
null=True, |
|||
blank=True, |
|||
verbose_name=_l("ComNpay Secret Key"), |
|||
) |
|||
minimum_payment = models.DecimalField( |
|||
verbose_name=_l("Minimum payment"), |
|||
help_text=_l("The minimal amount of money you have to use when paying" |
|||
" with ComNpay"), |
|||
max_digits=5, |
|||
decimal_places=2, |
|||
default=1, |
|||
) |
|||
|
|||
def end_payment(self, invoice, request): |
|||
""" |
|||
Build a request to start the negociation with Comnpay by using |
|||
a facture id, the price and the secret transaction data stored in |
|||
the preferences. |
|||
""" |
|||
invoice.valid = False |
|||
invoice.save() |
|||
host = request.get_host() |
|||
p = Transaction( |
|||
str(self.payment_credential), |
|||
str(self.payment_pass), |
|||
'https://' + host + reverse( |
|||
'cotisations:comnpay:accept_payment', |
|||
kwargs={'factureid': invoice.id} |
|||
), |
|||
'https://' + host + reverse('cotisations:comnpay:refuse_payment'), |
|||
'https://' + host + reverse('cotisations:comnpay:ipn'), |
|||
"", |
|||
"D" |
|||
) |
|||
r = { |
|||
'action': 'https://secure.homologation.comnpay.com', |
|||
'method': 'POST', |
|||
'content': p.buildSecretHTML( |
|||
_("Pay invoice no : ")+str(invoice.id), |
|||
invoice.prix_total(), |
|||
idTransaction=str(invoice.id) |
|||
), |
|||
'amount': invoice.prix_total(), |
|||
} |
|||
return render(request, 'cotisations/payment.html', r) |
|||
|
|||
def check_price(self, price, *args, **kwargs): |
|||
"""Checks that the price meets the requirement to be paid with ComNpay. |
|||
""" |
|||
return ((price >= self.minimum_payment), |
|||
_('In order to pay your invoice with ComNpay' |
|||
', the price must be grater than {} €') |
|||
.format(self.minimum_payment)) |
|||
@ -0,0 +1,40 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.conf.urls import url |
|||
from . import views |
|||
|
|||
urlpatterns = [ |
|||
url( |
|||
r'^accept/(?P<factureid>[0-9]+)$', |
|||
views.accept_payment, |
|||
name='accept_payment' |
|||
), |
|||
url( |
|||
r'^refuse/$', |
|||
views.refuse_payment, |
|||
name='refuse_payment' |
|||
), |
|||
url( |
|||
r'^ipn/$', |
|||
views.ipn, |
|||
name='ipn' |
|||
), |
|||
] |
|||
@ -0,0 +1,115 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django import forms |
|||
from django.utils.translation import ugettext as _ |
|||
from django.utils.translation import ugettext_lazy as _l |
|||
|
|||
from . import PAYMENT_METHODS |
|||
from cotisations.utils import find_payment_method |
|||
|
|||
def payment_method_factory(payment, *args, creation=True, **kwargs): |
|||
"""This function finds the right payment method form for a given payment. |
|||
|
|||
If the payment has a payment method, returns a ModelForm of it. Else if |
|||
it is the creation of the payment, a `PaymentMethodForm`. |
|||
Else `None`. |
|||
|
|||
Args: |
|||
payment: The payment |
|||
*args: arguments passed to the form |
|||
creation: Should be True if you are creating the payment |
|||
**kwargs: passed to the form |
|||
|
|||
Returns: |
|||
A form or None |
|||
""" |
|||
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 |
|||
) |
|||
elif creation: |
|||
return PaymentMethodForm(*args, **kwargs) |
|||
|
|||
|
|||
class PaymentMethodForm(forms.Form): |
|||
"""A special form which allows you to add a payment method to a `Payment` |
|||
object. |
|||
""" |
|||
|
|||
payment_method = forms.ChoiceField( |
|||
label=_l("Special payment method"), |
|||
help_text=_l("Warning : You will not be able to change the payment " |
|||
"method later. But you will be allowed to edit the other " |
|||
"options." |
|||
), |
|||
required=False |
|||
) |
|||
|
|||
def __init__(self, *args, **kwargs): |
|||
super(PaymentMethodForm, self).__init__(*args, **kwargs) |
|||
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 |
|||
] |
|||
|
|||
def clean(self): |
|||
"""A classic `clean` method, except that it replaces |
|||
`self.payment_method` by the payment method object if one has been |
|||
found. Tries to call `payment_method.valid_form` if it exists. |
|||
""" |
|||
super(PaymentMethodForm, self).clean() |
|||
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) |
|||
self.payment_method = form.save(commit=False) |
|||
if hasattr(self.payment_method, 'valid_form'): |
|||
self.payment_method.valid_form(self) |
|||
return self.cleaned_data |
|||
|
|||
|
|||
|
|||
def save(self, payment, *args, **kwargs): |
|||
"""Saves the payment method. |
|||
|
|||
Tries to call `payment_method.alter_payment` if it exists. |
|||
""" |
|||
commit = kwargs.pop('commit', True) |
|||
if not hasattr(self, 'payment_method'): |
|||
return None |
|||
self.payment_method.payment = payment |
|||
if hasattr(self.payment_method, 'alter_payment'): |
|||
self.payment_method.alter_payment(payment) |
|||
if commit: |
|||
payment.save() |
|||
self.payment_method.save() |
|||
return self.payment_method |
|||
@ -0,0 +1,33 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
|
|||
|
|||
class PaymentMethodMixin: |
|||
"""A simple mixin to avoid redefining end_payment if you don't need to""" |
|||
|
|||
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) |
|||
@ -0,0 +1,27 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
from django.conf.urls import include, url |
|||
from . import comnpay, cheque |
|||
|
|||
urlpatterns = [ |
|||
url(r'^comnpay/', include(comnpay.urls, namespace='comnpay')), |
|||
url(r'^cheque/', include(cheque.urls, namespace='cheque')), |
|||
] |
|||
@ -1,164 +0,0 @@ |
|||
{% extends "cotisations/sidebar.html" %} |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2017 Gabriel Détraz |
|||
Copyright © 2017 Goulven Kermarec |
|||
Copyright © 2017 Augustin Lemesle |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load staticfiles%} |
|||
{% load i18n %} |
|||
|
|||
{% block title %}{% trans "Invoices creation and edition" %}{% endblock %} |
|||
|
|||
{% block content %} |
|||
{% bootstrap_form_errors factureform %} |
|||
|
|||
<form class="form" method="post"> |
|||
{% csrf_token %} |
|||
<h3>{% trans "New invoice" %}</h3> |
|||
<p> |
|||
{% blocktrans %} |
|||
User's balance : {{ user.solde }} € |
|||
{% endblocktrans %} |
|||
</p> |
|||
{% bootstrap_form factureform %} |
|||
{{ venteform.management_form }} |
|||
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select --> |
|||
<h3>{% trans "Invoice's articles" %}</h3> |
|||
<div id="form_set" class="form-group"> |
|||
{% for form in venteform.forms %} |
|||
<div class='product_to_sell form-inline'> |
|||
{% trans "Article" %} : |
|||
{% bootstrap_form form label_class='sr-only' %} |
|||
|
|||
<button class="btn btn-danger btn-sm" id="id_form-0-article-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button> |
|||
</div> |
|||
{% endfor %} |
|||
</div> |
|||
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add an article"%}" id="add_one"> |
|||
<p> |
|||
{% blocktrans %} |
|||
Total price : <span id="total_price">0,00</span> € |
|||
{% endblocktrans %} |
|||
</p> |
|||
{% trans "Create" as tr_create %} |
|||
{% bootstrap_button tr_create button_type='submit' icon='star' %} |
|||
</form> |
|||
|
|||
<script type="text/javascript"> |
|||
|
|||
var prices = {}; |
|||
{% for article in articlelist %} |
|||
prices[{{ article.id|escapejs }}] = {{ article.prix }}; |
|||
{% endfor %} |
|||
|
|||
var template = `Article : |
|||
{% bootstrap_form venteform.empty_form label_class='sr-only' %} |
|||
|
|||
<button class="btn btn-danger btn-sm" |
|||
id="id_form-__prefix__-article-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button>` |
|||
|
|||
function add_article(){ |
|||
// Index start at 0 => new_index = number of items |
|||
var new_index = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
document.getElementById('id_form-TOTAL_FORMS').value ++; |
|||
var new_article = document.createElement('div'); |
|||
new_article.className = 'product_to_sell form-inline'; |
|||
new_article.innerHTML = template.replace(/__prefix__/g, new_index); |
|||
document.getElementById('form_set').appendChild(new_article); |
|||
add_listenner_for_id(new_index); |
|||
} |
|||
|
|||
function update_price(){ |
|||
var price = 0; |
|||
var product_count = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
var article, article_price, quantity; |
|||
for (i = 0; i < product_count; ++i){ |
|||
article = document.getElementById( |
|||
'id_form-' + i.toString() + '-article').value; |
|||
if (article == '') { |
|||
continue; |
|||
} |
|||
article_price = prices[article]; |
|||
quantity = document.getElementById( |
|||
'id_form-' + i.toString() + '-quantity').value; |
|||
price += article_price * quantity; |
|||
} |
|||
document.getElementById('total_price').innerHTML = |
|||
price.toFixed(2).toString().replace('.', ','); |
|||
} |
|||
|
|||
function add_listenner_for_id(i){ |
|||
document.getElementById('id_form-' + i.toString() + '-article') |
|||
.addEventListener("change", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-article') |
|||
.addEventListener("onkeypress", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-quantity') |
|||
.addEventListener("change", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-article-remove') |
|||
.addEventListener("click", function(event) { |
|||
var article = event.target.parentNode; |
|||
article.parentNode.removeChild(article); |
|||
document.getElementById('id_form-TOTAL_FORMS').value --; |
|||
update_price(); |
|||
} |
|||
) |
|||
} |
|||
|
|||
function set_cheque_info_visibility() { |
|||
var paiement = document.getElementById("id_Facture-paiement"); |
|||
var visible = paiement.value == paiement.getAttribute('data-cheque'); |
|||
p = document.getElementById("id_Facture-paiement"); |
|||
var display = 'none'; |
|||
if (visible) { |
|||
display = 'block'; |
|||
} |
|||
document.getElementById("id_Facture-cheque") |
|||
.parentNode.style.display = display; |
|||
document.getElementById("id_Facture-banque") |
|||
.parentNode.style.display = display; |
|||
} |
|||
|
|||
// Add events manager when DOM is fully loaded |
|||
document.addEventListener("DOMContentLoaded", function() { |
|||
document.getElementById("add_one") |
|||
.addEventListener("click", add_article, true); |
|||
var product_count = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
for (i = 0; i < product_count; ++i){ |
|||
add_listenner_for_id(i); |
|||
} |
|||
document.getElementById("id_Facture-paiement") |
|||
.addEventListener("change", set_cheque_info_visibility, true); |
|||
set_cheque_info_visibility(); |
|||
update_price(); |
|||
}); |
|||
|
|||
</script> |
|||
|
|||
{% endblock %} |
|||
@ -1,158 +0,0 @@ |
|||
{% extends "cotisations/sidebar.html" %} |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2017 Gabriel Détraz |
|||
Copyright © 2017 Goulven Kermarec |
|||
Copyright © 2017 Augustin Lemesle |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load staticfiles%} |
|||
{% load i18n %} |
|||
|
|||
{% block title %}{% trans "Invoices creation and edition" %}{% endblock %} |
|||
|
|||
{% block content %} |
|||
{% bootstrap_form_errors venteform.management_form %} |
|||
|
|||
<form class="form" method="post"> |
|||
{% csrf_token %} |
|||
<h3>{% trans "New invoice" %}</h3> |
|||
{{ venteform.management_form }} |
|||
<!-- TODO: FIXME to include data-type="check" for right option in id_cheque select --> |
|||
<h3>{% trans "Invoice's articles" %}</h3> |
|||
<div id="form_set" class="form-group"> |
|||
{% for form in venteform.forms %} |
|||
<div class='product_to_sell form-inline'> |
|||
{% trans "Article" %} : |
|||
{% bootstrap_form form label_class='sr-only' %} |
|||
|
|||
<button class="btn btn-danger btn-sm" id="id_form-0-article-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button> |
|||
</div> |
|||
{% endfor %} |
|||
</div> |
|||
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add an article"%}" id="add_one"> |
|||
<p> |
|||
{% blocktrans %} |
|||
Total price : <span id="total_price">0,00</span> € |
|||
{% endblocktrans %} |
|||
</p> |
|||
{% trans "Confirm" as tr_confirm %} |
|||
{% bootstrap_button tr_confirm button_type='submit' icon='star' %} |
|||
</form> |
|||
|
|||
<script type="text/javascript"> |
|||
|
|||
var prices = {}; |
|||
{% for article in articlelist %} |
|||
prices[{{ article.id|escapejs }}] = {{ article.prix }}; |
|||
{% endfor %} |
|||
|
|||
var template = `Article : |
|||
{% bootstrap_form venteform.empty_form label_class='sr-only' %} |
|||
|
|||
<button class="btn btn-danger btn-sm" id="id_form-__prefix__-article-remove" type="button"> |
|||
<span class="fa fa-times"></span> |
|||
</button>` |
|||
|
|||
function add_article(){ |
|||
// Index start at 0 => new_index = number of items |
|||
var new_index = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
document.getElementById('id_form-TOTAL_FORMS').value ++; |
|||
var new_article = document.createElement('div'); |
|||
new_article.className = 'product_to_sell form-inline'; |
|||
new_article.innerHTML = template.replace(/__prefix__/g, new_index); |
|||
document.getElementById('form_set').appendChild(new_article); |
|||
add_listenner_for_id(new_index); |
|||
} |
|||
|
|||
function update_price(){ |
|||
var price = 0; |
|||
var product_count = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
var article, article_price, quantity; |
|||
for (i = 0; i < product_count; ++i){ |
|||
article = document.getElementById( |
|||
'id_form-' + i.toString() + '-article').value; |
|||
if (article == '') { |
|||
continue; |
|||
} |
|||
article_price = prices[article]; |
|||
quantity = document.getElementById( |
|||
'id_form-' + i.toString() + '-quantity').value; |
|||
price += article_price * quantity; |
|||
} |
|||
document.getElementById('total_price').innerHTML = |
|||
price.toFixed(2).toString().replace('.', ','); |
|||
} |
|||
|
|||
function add_listenner_for_id(i){ |
|||
document.getElementById('id_form-' + i.toString() + '-article') |
|||
.addEventListener("change", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-article') |
|||
.addEventListener("onkeypress", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-quantity') |
|||
.addEventListener("change", update_price, true); |
|||
document.getElementById('id_form-' + i.toString() + '-article-remove') |
|||
.addEventListener("click", function(event) { |
|||
var article = event.target.parentNode; |
|||
article.parentNode.removeChild(article); |
|||
document.getElementById('id_form-TOTAL_FORMS').value --; |
|||
update_price(); |
|||
} |
|||
) |
|||
} |
|||
|
|||
function set_cheque_info_visibility() { |
|||
var paiement = document.getElementById("id_Facture-paiement"); |
|||
var visible = paiement.value == paiement.getAttribute('data-cheque'); |
|||
p = document.getElementById("id_Facture-paiement"); |
|||
var display = 'none'; |
|||
if (visible) { |
|||
display = 'block'; |
|||
} |
|||
document.getElementById("id_Facture-cheque") |
|||
.parentNode.style.display = display; |
|||
document.getElementById("id_Facture-banque") |
|||
.parentNode.style.display = display; |
|||
} |
|||
|
|||
// Add events manager when DOM is fully loaded |
|||
document.addEventListener("DOMContentLoaded", function() { |
|||
document.getElementById("add_one") |
|||
.addEventListener("click", add_article, true); |
|||
var product_count = |
|||
document.getElementsByClassName('product_to_sell').length; |
|||
for (i = 0; i < product_count; ++i){ |
|||
add_listenner_for_id(i); |
|||
} |
|||
document.getElementById("id_Facture-paiement") |
|||
.addEventListener("change", set_cheque_info_visibility, true); |
|||
set_cheque_info_visibility(); |
|||
update_price(); |
|||
}); |
|||
|
|||
</script> |
|||
|
|||
{% endblock %} |
|||
|
|||
@ -1,45 +0,0 @@ |
|||
{% extends "cotisations/sidebar.html" %} |
|||
{% comment %} |
|||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
se veut agnostique au réseau considéré, de manière à être installable en |
|||
quelques clics. |
|||
|
|||
Copyright © 2017 Gabriel Détraz |
|||
Copyright © 2017 Goulven Kermarec |
|||
Copyright © 2017 Augustin Lemesle |
|||
|
|||
This program is free software; you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License along |
|||
with this program; if not, write to the Free Software Foundation, Inc., |
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
{% endcomment %} |
|||
|
|||
{% load bootstrap3 %} |
|||
{% load staticfiles%} |
|||
{% load i18n %} |
|||
|
|||
{% block title %}{% trans "Balance refill" %}{% endblock %} |
|||
|
|||
{% block content %} |
|||
<h2>{% trans "Balance refill" %}</h2> |
|||
<h3> |
|||
{% blocktrans %} |
|||
Balance : <span class="label label-default">{{ request.user.solde }} €</span> |
|||
{% endblocktrans %} |
|||
</h3> |
|||
<form class="form" method="post"> |
|||
{% csrf_token %} |
|||
{% bootstrap_form rechargeform %} |
|||
{% trans "Confirm" as tr_confirm %} |
|||
{% bootstrap_button tr_confirm button_type='submit' icon='piggy-bank' %} |
|||
</form> |
|||
{% endblock %} |
|||
@ -0,0 +1,32 @@ |
|||
# -*- mode: python; coding: utf-8 -*- |
|||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il |
|||
# se veut agnostique au réseau considéré, de manière à être installable en |
|||
# quelques clics. |
|||
# |
|||
# Copyright © 2018 Hugo Levy-Falk |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of the GNU General Public License as published by |
|||
# the Free Software Foundation; either version 2 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 General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License along |
|||
# with this program; if not, write to the Free Software Foundation, Inc., |
|||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|||
|
|||
|
|||
def find_payment_method(payment): |
|||
"""Finds the payment method associated to the payment if it exists.""" |
|||
from cotisations.payment_methods import PAYMENT_METHODS |
|||
for method in PAYMENT_METHODS: |
|||
try: |
|||
o = method.PaymentMethod.objects.get(payment=payment) |
|||
return o |
|||
except method.PaymentMethod.DoesNotExist: |
|||
pass |
|||
return None |
|||
@ -0,0 +1,21 @@ |
|||
from django.forms import ValidationError |
|||
from django.utils.translation import ugettext as _ |
|||
|
|||
|
|||
def check_no_balance(is_balance): |
|||
"""This functions checks that no Paiement with is_balance=True exists |
|||
|
|||
Args: |
|||
is_balance: True if the model is balance. |
|||
|
|||
Raises: |
|||
ValidationError: if such a Paiement exists. |
|||
""" |
|||
from .models import Paiement |
|||
if not is_balance: |
|||
return |
|||
p = Paiement.objects.filter(is_balance=True) |
|||
if len(p) > 0: |
|||
raise ValidationError( |
|||
_("There are already payment method(s) for user balance") |
|||
) |
|||
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-06-17 15:12 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0034_auto_20180416_1120'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='optionaluser', |
|||
name='allow_self_subscription', |
|||
field=models.BooleanField(default=False, help_text="Autoriser les utilisateurs à cotiser par eux mêmes via les moyens de paiement permettant l'auto-cotisation."), |
|||
), |
|||
] |
|||
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-07-05 13:40 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0035_optionaluser_allow_self_subscription'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RemoveField( |
|||
model_name='assooption', |
|||
name='payment_pass', |
|||
), |
|||
] |
|||
|
|||
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.10.7 on 2018-07-05 13:40 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('preferences', '0044_remove_payment_pass'), |
|||
('cotisations', '0030_custom_payment'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RemoveField( |
|||
model_name='assooption', |
|||
name='payment', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='assooption', |
|||
name='payment_id', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='optionaluser', |
|||
name='allow_self_subscription', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='optionaluser', |
|||
name='max_solde', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='optionaluser', |
|||
name='min_online_payment', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='optionaluser', |
|||
name='solde_negatif', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='optionaluser', |
|||
name='user_solde', |
|||
), |
|||
] |
|||
Loading…
Reference in new issue