mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-08-19 13:43:40 +00:00
ACL sur les paiements.
This commit is contained in:
parent
5f1e2380c8
commit
5887eb68ae
6 changed files with 141 additions and 59 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -94,3 +94,14 @@ If you to restrict the IP which can see the debug, use the `INTERNAL_IPS` option
|
|||
```
|
||||
INTERNAL_IPS = ["10.0.0.1", "10.0.0.2"]
|
||||
```
|
||||
|
||||
## MR 174 : Fix online payment + allow users to pay their subscription
|
||||
|
||||
Add the possibility to use custom payment methods. There is also a boolean field on the
|
||||
Payments allowing every user to use some kinds of payment. You have to add the rights `cotisations.use_every_payment` and `cotisations.buy_every_article`
|
||||
to the staff members so they can use every type of payment to buy anything.
|
||||
|
||||
Don't forget to run migrations, several settings previously in the `preferences` app ar now
|
||||
in their own Payment models.
|
||||
|
||||
To have a closer look on how the payments works, pleas go to the wiki.
|
||||
|
|
|
@ -53,16 +53,16 @@ class NewFactureForm(FormRevMixin, ModelForm):
|
|||
Form used to create a new invoice by using a payment method, a bank and a
|
||||
cheque number.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
allowed_payment = kwargs.pop('allowed_payment', None)
|
||||
super(NewFactureForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
# TODO : remove the use of cheque and banque and paiement
|
||||
# for something more generic or at least in English
|
||||
if allowed_payment:
|
||||
self.fields['paiement'].queryset = allowed_payment
|
||||
self.fields['paiement'].empty_label = \
|
||||
_("Select a payment method")
|
||||
self.fields['paiement'].queryset = Paiement.objects.filter(
|
||||
pk__in=map(lambda x: x.pk, Paiement.find_allowed_payments(user))
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Facture
|
||||
|
@ -71,16 +71,10 @@ class NewFactureForm(FormRevMixin, ModelForm):
|
|||
def clean(self):
|
||||
cleaned_data = super(NewFactureForm, self).clean()
|
||||
paiement = cleaned_data.get('paiement')
|
||||
cheque = cleaned_data.get('cheque')
|
||||
banque = cleaned_data.get('banque')
|
||||
if not paiement:
|
||||
raise forms.ValidationError(
|
||||
_("A payment method must be specified.")
|
||||
)
|
||||
elif paiement.type_paiement == 'check' and not (cheque and banque):
|
||||
raise forms.ValidationError(
|
||||
_("A cheque number and a bank must be specified.")
|
||||
)
|
||||
return cleaned_data
|
||||
|
||||
|
||||
|
@ -103,8 +97,7 @@ class CreditSoldeForm(NewFactureForm):
|
|||
montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True)
|
||||
|
||||
|
||||
class SelectUserArticleForm(
|
||||
FormRevMixin, Form):
|
||||
class SelectUserArticleForm(FormRevMixin, Form):
|
||||
"""
|
||||
Form used to select an article during the creation of an invoice for a
|
||||
member.
|
||||
|
@ -123,12 +116,11 @@ class SelectUserArticleForm(
|
|||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self_subscription = kwargs.pop('is_self_subscription', False)
|
||||
user = kwargs.pop('user')
|
||||
super(SelectUserArticleForm, self).__init__(*args, **kwargs)
|
||||
if self_subscription:
|
||||
self.fields['article'].queryset = Article.objects.filter(
|
||||
Q(type_user='All') | Q(type_user='Adherent')
|
||||
).filter(allow_self_subscription=True)
|
||||
pk__in=map(lambda x: x.pk, Article.find_allowed_articles(user))
|
||||
)
|
||||
|
||||
|
||||
class SelectClubArticleForm(Form):
|
||||
|
@ -150,12 +142,11 @@ class SelectClubArticleForm(Form):
|
|||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self_subscription = kwargs.pop('is_self_subscription', False)
|
||||
user = kwargs.pop('user')
|
||||
super(SelectClubArticleForm, self).__init__(*args, **kwargs)
|
||||
if self_subscription:
|
||||
self.fields['article'].queryset = Article.objects.filter(
|
||||
Q(type_user='All') | Q(type_user='Club')
|
||||
).filter(allow_self_subscription=True)
|
||||
pk__in=map(lambda x: x.pk, Article.find_allowed_articles(user))
|
||||
)
|
||||
|
||||
|
||||
# TODO : change Facture to Invoice
|
||||
|
@ -242,7 +233,7 @@ class PaiementForm(FormRevMixin, ModelForm):
|
|||
class Meta:
|
||||
model = Paiement
|
||||
# TODO : change moyen to method and type_paiement to payment_type
|
||||
fields = ['moyen', 'allow_self_subscription']
|
||||
fields = ['moyen', 'available_for_everyone']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
|
@ -315,6 +306,7 @@ class NewFactureSoldeForm(NewFactureForm):
|
|||
"""
|
||||
Form used to create an invoice
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(NewFactureSoldeForm, self).__init__(
|
||||
|
|
28
cotisations/migrations/0035_auto_20180703_1005.py
Normal file
28
cotisations/migrations/0035_auto_20180703_1005.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-07-03 15:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0034_auto_20180703_0929'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='paiement',
|
||||
options={'permissions': (('view_paiement', "Can see a payement's details"), ('use', 'Can use a payement')), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='paiement',
|
||||
name='allow_self_subscription',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='paiement',
|
||||
name='available_for_everyone',
|
||||
field=models.BooleanField(default=False, verbose_name='Is available for every user'),
|
||||
),
|
||||
]
|
28
cotisations/migrations/0036_auto_20180703_1056.py
Normal file
28
cotisations/migrations/0036_auto_20180703_1056.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2018-07-03 15:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cotisations', '0035_auto_20180703_1005'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
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.RemoveField(
|
||||
model_name='article',
|
||||
name='allow_self_subscription',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='available_for_everyone',
|
||||
field=models.BooleanField(default=False, verbose_name='Is available for every user'),
|
||||
),
|
||||
]
|
|
@ -49,7 +49,6 @@ from django.contrib import messages
|
|||
from machines.models import regen
|
||||
from re2o.field_permissions import FieldPermissionModelMixin
|
||||
from re2o.mixins import AclMixin, RevMixin
|
||||
from preferences.models import OptionalUser
|
||||
|
||||
from cotisations.utils import find_payment_method
|
||||
|
||||
|
@ -227,10 +226,11 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
|||
:return: a message and a boolean which is True if the user can create
|
||||
an invoice or if the `options.allow_self_subscription` is set.
|
||||
"""
|
||||
if OptionalUser.get_cached_value('allow_self_subscription'):
|
||||
return True, None
|
||||
nb_payments = len(Paiement.find_allowed_payments(user_request))
|
||||
nb_articles = len(Article.find_allowed_articles(user_request))
|
||||
return (
|
||||
user_request.has_perm('cotisations.add_facture'),
|
||||
user_request.has_perm('cotisations.add_facture')
|
||||
or (nb_payments*nb_articles),
|
||||
_("You don't have the right to create an invoice.")
|
||||
)
|
||||
|
||||
|
@ -522,9 +522,9 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
max_length=255,
|
||||
verbose_name=_l("Type of cotisation")
|
||||
)
|
||||
allow_self_subscription = models.BooleanField(
|
||||
available_for_everyone = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_l("Is available for self subscription")
|
||||
verbose_name=_l("Is available for every user")
|
||||
)
|
||||
|
||||
unique_together = ('name', 'type_user')
|
||||
|
@ -532,6 +532,7 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
class Meta:
|
||||
permissions = (
|
||||
('view_article', _l("Can see an article's details")),
|
||||
('buy_every_article', _l("Can buy every_article"))
|
||||
)
|
||||
verbose_name = "Article"
|
||||
verbose_name_plural = "Articles"
|
||||
|
@ -549,6 +550,24 @@ class Article(RevMixin, AclMixin, models.Model):
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def can_buy_article(self, user, *_args, **_kwargs):
|
||||
"""Check if a user can buy this article.
|
||||
|
||||
:param self: The article
|
||||
:param user: The user requesting buying
|
||||
:returns: A boolean stating if usage is granted and an explanation
|
||||
message if the boolean is `False`.
|
||||
"""
|
||||
return (
|
||||
self.available_for_everyone
|
||||
or user.has_perm('cotisations.buy_every_article'),
|
||||
_("You cannot use this Payment.")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def find_allowed_articles(cls, user):
|
||||
return [p for p in cls.objects.all() if p.can_buy_article(user)[0]]
|
||||
|
||||
|
||||
class Banque(RevMixin, AclMixin, models.Model):
|
||||
"""
|
||||
|
@ -601,14 +620,15 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
|||
default=0,
|
||||
verbose_name=_l("Payment type")
|
||||
)
|
||||
allow_self_subscription = models.BooleanField(
|
||||
available_for_everyone = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_l("Is available for self subscription")
|
||||
verbose_name=_l("Is available for every user")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
('view_paiement', _l("Can see a payement's details")),
|
||||
('use_every_payment', _l("Can use every payement")),
|
||||
)
|
||||
verbose_name = _l("Payment method")
|
||||
verbose_name_plural = _l("Payment methods")
|
||||
|
@ -671,6 +691,24 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
|||
kwargs={'userid': invoice.user.pk}
|
||||
))
|
||||
|
||||
def can_use_payment(self, user, *_args, **_kwargs):
|
||||
"""Check if a user can use this payment.
|
||||
|
||||
:param self: The payment
|
||||
:param user: The user requesting usage
|
||||
:returns: A boolean stating if usage is granted and an explanation
|
||||
message if the boolean is `False`.
|
||||
"""
|
||||
return (
|
||||
self.available_for_everyone
|
||||
or user.has_perm('cotisations.use_every_payment'),
|
||||
_("You cannot use this Payment.")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def find_allowed_payments(cls, user):
|
||||
return [p for p in cls.objects.all() if p.can_use_payment(user)[0]]
|
||||
|
||||
|
||||
class Cotisation(RevMixin, AclMixin, models.Model):
|
||||
"""
|
||||
|
|
|
@ -98,40 +98,22 @@ def new_facture(request, user, userid):
|
|||
article_list = Article.objects.filter(
|
||||
Q(type_user='All') | Q(type_user=request.user.class_name)
|
||||
)
|
||||
# Building the invocie form and the article formset
|
||||
is_self_subscription = False
|
||||
can_create_invoice = request.user.has_perm('cotisations.add_facture')
|
||||
allow_self_subscription = OptionalUser.get_cached_value(
|
||||
'allow_self_subscription')
|
||||
if not can_create_invoice:
|
||||
if allow_self_subscription:
|
||||
is_self_subscription = True
|
||||
article_list = article_list.filter(allow_self_subscription=True)
|
||||
allowed_payment = Paiement.objects.filter(
|
||||
allow_self_subscription=True)
|
||||
# Building the invoice form and the article formset
|
||||
invoice_form = NewFactureForm(
|
||||
request.POST or None, instance=invoice, allowed_payment=allowed_payment)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
_("You cannot subscribe. Please ask to the staff.")
|
||||
request.POST or None,
|
||||
instance=invoice,
|
||||
user=request.user
|
||||
)
|
||||
return redirect(reverse(
|
||||
'users:profil',
|
||||
kwargs={'userid': userid}
|
||||
))
|
||||
else:
|
||||
invoice_form = NewFactureForm(request.POST or None, instance=invoice)
|
||||
|
||||
if request.user.is_class_club:
|
||||
article_formset = formset_factory(SelectClubArticleForm)(
|
||||
request.POST or None,
|
||||
form_kwargs={'is_self_subscription': is_self_subscription}
|
||||
form_kwargs={'user': request.user}
|
||||
)
|
||||
else:
|
||||
article_formset = formset_factory(SelectUserArticleForm)(
|
||||
request.POST or None,
|
||||
form_kwargs={'is_self_subscription': is_self_subscription}
|
||||
form_kwargs={'user': request.user}
|
||||
)
|
||||
|
||||
if invoice_form.is_valid() and article_formset.is_valid():
|
||||
|
@ -156,7 +138,10 @@ def new_facture(request, user, userid):
|
|||
)
|
||||
new_purchase.save()
|
||||
|
||||
return new_invoice_instance.paiement.end_payment(new_invoice_instance, request)
|
||||
return new_invoice_instance.paiement.end_payment(
|
||||
new_invoice_instance,
|
||||
request
|
||||
)
|
||||
|
||||
messages.error(
|
||||
request,
|
||||
|
|
Loading…
Reference in a new issue