8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-23 23:43:47 +00:00

ACL sur les paiements.

This commit is contained in:
Hugo LEVY-FALK 2018-07-03 18:53:44 +02:00
parent 5f1e2380c8
commit 5887eb68ae
6 changed files with 141 additions and 59 deletions

View file

@ -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"] 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.

View file

@ -53,16 +53,16 @@ class NewFactureForm(FormRevMixin, ModelForm):
Form used to create a new invoice by using a payment method, a bank and a Form used to create a new invoice by using a payment method, a bank and a
cheque number. cheque number.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
allowed_payment = kwargs.pop('allowed_payment', None)
super(NewFactureForm, self).__init__(*args, prefix=prefix, **kwargs) 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 = \ self.fields['paiement'].empty_label = \
_("Select a payment method") _("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: class Meta:
model = Facture model = Facture
@ -71,16 +71,10 @@ class NewFactureForm(FormRevMixin, ModelForm):
def clean(self): def clean(self):
cleaned_data = super(NewFactureForm, self).clean() cleaned_data = super(NewFactureForm, self).clean()
paiement = cleaned_data.get('paiement') paiement = cleaned_data.get('paiement')
cheque = cleaned_data.get('cheque')
banque = cleaned_data.get('banque')
if not paiement: if not paiement:
raise forms.ValidationError( raise forms.ValidationError(
_("A payment method must be specified.") _("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 return cleaned_data
@ -103,8 +97,7 @@ class CreditSoldeForm(NewFactureForm):
montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True) montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True)
class SelectUserArticleForm( class SelectUserArticleForm(FormRevMixin, Form):
FormRevMixin, Form):
""" """
Form used to select an article during the creation of an invoice for a Form used to select an article during the creation of an invoice for a
member. member.
@ -123,12 +116,11 @@ class SelectUserArticleForm(
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self_subscription = kwargs.pop('is_self_subscription', False) user = kwargs.pop('user')
super(SelectUserArticleForm, self).__init__(*args, **kwargs) super(SelectUserArticleForm, self).__init__(*args, **kwargs)
if self_subscription:
self.fields['article'].queryset = Article.objects.filter( self.fields['article'].queryset = Article.objects.filter(
Q(type_user='All') | Q(type_user='Adherent') pk__in=map(lambda x: x.pk, Article.find_allowed_articles(user))
).filter(allow_self_subscription=True) )
class SelectClubArticleForm(Form): class SelectClubArticleForm(Form):
@ -150,12 +142,11 @@ class SelectClubArticleForm(Form):
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self_subscription = kwargs.pop('is_self_subscription', False) user = kwargs.pop('user')
super(SelectClubArticleForm, self).__init__(*args, **kwargs) super(SelectClubArticleForm, self).__init__(*args, **kwargs)
if self_subscription:
self.fields['article'].queryset = Article.objects.filter( self.fields['article'].queryset = Article.objects.filter(
Q(type_user='All') | Q(type_user='Club') pk__in=map(lambda x: x.pk, Article.find_allowed_articles(user))
).filter(allow_self_subscription=True) )
# TODO : change Facture to Invoice # TODO : change Facture to Invoice
@ -242,7 +233,7 @@ class PaiementForm(FormRevMixin, ModelForm):
class Meta: class Meta:
model = Paiement model = Paiement
# TODO : change moyen to method and type_paiement to payment_type # 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): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
@ -315,6 +306,7 @@ class NewFactureSoldeForm(NewFactureForm):
""" """
Form used to create an invoice Form used to create an invoice
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NewFactureSoldeForm, self).__init__( super(NewFactureSoldeForm, self).__init__(

View 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'),
),
]

View 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'),
),
]

View file

@ -49,7 +49,6 @@ from django.contrib import messages
from machines.models import regen from machines.models import regen
from re2o.field_permissions import FieldPermissionModelMixin from re2o.field_permissions import FieldPermissionModelMixin
from re2o.mixins import AclMixin, RevMixin from re2o.mixins import AclMixin, RevMixin
from preferences.models import OptionalUser
from cotisations.utils import find_payment_method 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 :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. an invoice or if the `options.allow_self_subscription` is set.
""" """
if OptionalUser.get_cached_value('allow_self_subscription'): nb_payments = len(Paiement.find_allowed_payments(user_request))
return True, None nb_articles = len(Article.find_allowed_articles(user_request))
return ( 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.") _("You don't have the right to create an invoice.")
) )
@ -522,9 +522,9 @@ class Article(RevMixin, AclMixin, models.Model):
max_length=255, max_length=255,
verbose_name=_l("Type of cotisation") verbose_name=_l("Type of cotisation")
) )
allow_self_subscription = models.BooleanField( available_for_everyone = models.BooleanField(
default=False, default=False,
verbose_name=_l("Is available for self subscription") verbose_name=_l("Is available for every user")
) )
unique_together = ('name', 'type_user') unique_together = ('name', 'type_user')
@ -532,6 +532,7 @@ class Article(RevMixin, AclMixin, models.Model):
class Meta: class Meta:
permissions = ( permissions = (
('view_article', _l("Can see an article's details")), ('view_article', _l("Can see an article's details")),
('buy_every_article', _l("Can buy every_article"))
) )
verbose_name = "Article" verbose_name = "Article"
verbose_name_plural = "Articles" verbose_name_plural = "Articles"
@ -549,6 +550,24 @@ class Article(RevMixin, AclMixin, models.Model):
def __str__(self): def __str__(self):
return self.name 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): class Banque(RevMixin, AclMixin, models.Model):
""" """
@ -601,14 +620,15 @@ class Paiement(RevMixin, AclMixin, models.Model):
default=0, default=0,
verbose_name=_l("Payment type") verbose_name=_l("Payment type")
) )
allow_self_subscription = models.BooleanField( available_for_everyone = models.BooleanField(
default=False, default=False,
verbose_name=_l("Is available for self subscription") verbose_name=_l("Is available for every user")
) )
class Meta: class Meta:
permissions = ( permissions = (
('view_paiement', _l("Can see a payement's details")), ('view_paiement', _l("Can see a payement's details")),
('use_every_payment', _l("Can use every payement")),
) )
verbose_name = _l("Payment method") verbose_name = _l("Payment method")
verbose_name_plural = _l("Payment methods") verbose_name_plural = _l("Payment methods")
@ -671,6 +691,24 @@ class Paiement(RevMixin, AclMixin, models.Model):
kwargs={'userid': invoice.user.pk} 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): class Cotisation(RevMixin, AclMixin, models.Model):
""" """

View file

@ -98,40 +98,22 @@ def new_facture(request, user, userid):
article_list = Article.objects.filter( article_list = Article.objects.filter(
Q(type_user='All') | Q(type_user=request.user.class_name) Q(type_user='All') | Q(type_user=request.user.class_name)
) )
# Building the invocie form and the article formset # Building the invoice 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)
invoice_form = NewFactureForm( invoice_form = NewFactureForm(
request.POST or None, instance=invoice, allowed_payment=allowed_payment) request.POST or None,
else: instance=invoice,
messages.error( user=request.user
request,
_("You cannot subscribe. Please ask to the staff.")
) )
return redirect(reverse(
'users:profil',
kwargs={'userid': userid}
))
else:
invoice_form = NewFactureForm(request.POST or None, instance=invoice)
if request.user.is_class_club: if request.user.is_class_club:
article_formset = formset_factory(SelectClubArticleForm)( article_formset = formset_factory(SelectClubArticleForm)(
request.POST or None, request.POST or None,
form_kwargs={'is_self_subscription': is_self_subscription} form_kwargs={'user': request.user}
) )
else: else:
article_formset = formset_factory(SelectUserArticleForm)( article_formset = formset_factory(SelectUserArticleForm)(
request.POST or None, 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(): if invoice_form.is_valid() and article_formset.is_valid():
@ -156,7 +138,10 @@ def new_facture(request, user, userid):
) )
new_purchase.save() 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( messages.error(
request, request,