|
@@ -1,172 +1,170 @@
|
|
|
from django.contrib import admin
|
|
|
-from django.contrib.contenttypes.admin import GenericTabularInline
|
|
|
-from django.contrib.contenttypes.models import ContentType
|
|
|
-from django.db.models import Q
|
|
|
+from django.db import models
|
|
|
from django.core.urlresolvers import reverse
|
|
|
+from django.forms import BaseInlineFormSet
|
|
|
+from django.utils.html import format_html
|
|
|
|
|
|
from services.models import ServiceType
|
|
|
-from .models import Payment
|
|
|
+from services.admin import ServiceAdmin
|
|
|
+from adhesions.admin import AdhesionAdmin
|
|
|
+from .models import RecurringPayment, PaymentUpdate
|
|
|
+from .utils import notify_payment_update
|
|
|
|
|
|
|
|
|
### Inlines
|
|
|
|
|
|
-class PaymentInline(GenericTabularInline):
|
|
|
- model = Payment
|
|
|
- ct_field = 'reason_type'
|
|
|
- ct_fk_field = 'reason_id'
|
|
|
- extra = 0
|
|
|
- #max_num = 0
|
|
|
- fields = ('amount', 'period', 'payment_method', 'start', 'end',)
|
|
|
- #readonly_fields = ('amount', 'period', 'payment_method', 'start',)
|
|
|
- verbose_name_plural = 'Contributions'
|
|
|
-
|
|
|
- #def get_queryset(self, request):
|
|
|
- # # Paiement récurrent en cours (sans date de fin)
|
|
|
- # return super().get_queryset(request).filter(period__gt=0, end__isnull=True)
|
|
|
-
|
|
|
- #def has_delete_permission(self, request, obj=None):
|
|
|
- # return False
|
|
|
-
|
|
|
-
|
|
|
-#class ValidatedPaymentInline(PaymentInline):
|
|
|
-# extra = 0
|
|
|
-# verbose_name_plural = 'Paiements validés'
|
|
|
-#
|
|
|
-# def get_readonly_fields(self, request, obj=None):
|
|
|
-# if request.user.has_perm('banking.validate_payment'):
|
|
|
-# return ()
|
|
|
-# else:
|
|
|
-# return self.get_fields(request, obj)
|
|
|
-#
|
|
|
-# def has_add_permission(self, request):
|
|
|
-# return False
|
|
|
-#
|
|
|
-# def has_delete_permission(self, request, obj=None):
|
|
|
-# return request.user.has_perm('banking.validate_payment')
|
|
|
-#
|
|
|
-# def get_queryset(self, request):
|
|
|
-# return super().get_queryset(request).filter(validated=True)
|
|
|
-#
|
|
|
-#
|
|
|
-#class PendingOrNewPaymentInline(PaymentInline):
|
|
|
-# verbose_name_plural = 'Paiements en attente de validation et nouveaux paiements'
|
|
|
-#
|
|
|
-# def get_queryset(self, request):
|
|
|
-# return super().get_queryset(request).filter(validated=False)
|
|
|
-
|
|
|
-
|
|
|
-### Filters
|
|
|
-
|
|
|
class PaymentTypeFilter(admin.SimpleListFilter):
|
|
|
- title = 'type de paiement'
|
|
|
+ title = 'type'
|
|
|
parameter_name = 'type'
|
|
|
|
|
|
def lookups(self, request, model_admin):
|
|
|
- choices = [
|
|
|
- ('membership', 'Cotisation'),
|
|
|
+ return (
|
|
|
+ ('adhesion', 'Adhésion'),
|
|
|
('service', 'Service'),
|
|
|
- ]
|
|
|
- service_types = ServiceType.objects.all()
|
|
|
- if not (request.user.is_superuser or request.user.has_perm('banking.validate_payment')):
|
|
|
- service_types = service_types.filter(group__in=request.user.groups.all())
|
|
|
- for stype in service_types:
|
|
|
- choices.append((stype.pk, 'Service (%s)' % stype.name))
|
|
|
- return choices
|
|
|
+ )
|
|
|
|
|
|
def queryset(self, request, queryset):
|
|
|
- if self.value() == 'membership':
|
|
|
- return queryset.filter(reason_type__app_label='adhesions',
|
|
|
- reason_type__model='adhesion')
|
|
|
+ if self.value() == 'adhesion':
|
|
|
+ return queryset.filter(adhesion__isnull=False)
|
|
|
if self.value() == 'service':
|
|
|
- return queryset.filter(reason_type__app_label='services',
|
|
|
- reason_type__model='service')
|
|
|
- try:
|
|
|
- service_type = ServiceType.objects.get(pk=int(self.value()))
|
|
|
- except (ValueError, TypeError, ServiceType.DoesNotExist,):
|
|
|
- return queryset
|
|
|
- else:
|
|
|
- return queryset.filter(service__service_type=service_type)
|
|
|
+ return queryset.filter(service__isnull=False)
|
|
|
+
|
|
|
+
|
|
|
+class PaymentStatusFilter(admin.SimpleListFilter):
|
|
|
+ title = 'actif'
|
|
|
+ parameter_name = 'active'
|
|
|
+
|
|
|
+ def lookups(self, request, model_admin):
|
|
|
+ return (
|
|
|
+ (0, 'Inactif'),
|
|
|
+ (1, 'Actif'),
|
|
|
+ )
|
|
|
+
|
|
|
+ def queryset(self, request, queryset):
|
|
|
+ actives = PaymentUpdate.objects.filter(validated=True).order_by('payment', '-start') \
|
|
|
+ .distinct('payment').exclude(payment_method=PaymentUpdate.STOP) \
|
|
|
+ .values_list('payment__pk', flat=True)
|
|
|
+ if self.value() == '0':
|
|
|
+ return queryset.exclude(pk__in=actives)
|
|
|
+ if self.value() == '1':
|
|
|
+ return queryset.filter(pk__in=actives)
|
|
|
+
|
|
|
+
|
|
|
+class PendingPaymentFilter(admin.SimpleListFilter):
|
|
|
+ title = 'opérations en attente'
|
|
|
+ parameter_name = 'pending'
|
|
|
+
|
|
|
+ def lookups(self, request, model_admin):
|
|
|
+ return (
|
|
|
+ (0, 'À jour'),
|
|
|
+ (1, 'En attente'),
|
|
|
+ )
|
|
|
+
|
|
|
+ def queryset(self, request, queryset):
|
|
|
+ pending = PaymentUpdate.objects.filter(validated=False).values_list('payment__pk', flat=True)
|
|
|
+ if self.value() == '0':
|
|
|
+ return queryset.exclude(pk__in=pending)
|
|
|
+ if self.value() == '1':
|
|
|
+ return queryset.filter(pk__in=pending)
|
|
|
+
|
|
|
+
|
|
|
+### Inlines
|
|
|
+
|
|
|
+class PendingPaymentUpdateFormSet(BaseInlineFormSet):
|
|
|
+ def save_new(self, form, commit=True):
|
|
|
+ obj = super().save_new(form, commit)
|
|
|
+ notify_payment_update(self.request, obj)
|
|
|
+ return obj
|
|
|
+
|
|
|
+ def save_existing(self, form, instance, commit=True):
|
|
|
+ old = PaymentUpdate.objects.get(pk=instance.pk)
|
|
|
+ notify_payment_update(self.request, instance, old)
|
|
|
+ return super().save_existing(form, instance, commit)
|
|
|
+
|
|
|
+
|
|
|
+class PendingPaymentUpdateInline(admin.TabularInline):
|
|
|
+ model = PaymentUpdate
|
|
|
+ formset = PendingPaymentUpdateFormSet
|
|
|
+ extra = 1
|
|
|
+ verbose_name_plural = 'En attente de saisie bancaire'
|
|
|
+
|
|
|
+ def get_formset(self, request, obj=None, **kwargs):
|
|
|
+ formset = super().get_formset(request, obj, **kwargs)
|
|
|
+ formset.request = request
|
|
|
+ return formset
|
|
|
+
|
|
|
+ def get_queryset(self, request):
|
|
|
+ return super().get_queryset(request).filter(validated=False)
|
|
|
+
|
|
|
+
|
|
|
+class ValidatedPaymentUpdateInline(admin.TabularInline):
|
|
|
+ model = PaymentUpdate
|
|
|
+ verbose_name_plural = 'Historique'
|
|
|
+ max_num = 0
|
|
|
+ fields = ('amount', 'period', 'payment_method', 'start',)
|
|
|
+ readonly_fields = ('amount', 'period', 'payment_method', 'start',)
|
|
|
+
|
|
|
+ def has_delete_permission(self, request, obj=None):
|
|
|
+ return False
|
|
|
+
|
|
|
+ def get_queryset(self, request):
|
|
|
+ return super().get_queryset(request).filter(validated=True)
|
|
|
+
|
|
|
|
|
|
+### Helpers
|
|
|
|
|
|
-#### Actions
|
|
|
-#
|
|
|
-#def validate_payment(payment, request, queryset):
|
|
|
-# queryset.update(validated=True)
|
|
|
+def prefix_search_field(prefix, field):
|
|
|
+ if field[0] == '=':
|
|
|
+ return '=' + prefix + '__' + field[1:]
|
|
|
+ else:
|
|
|
+ return prefix + '__' + field
|
|
|
|
|
|
|
|
|
### ModelAdmin
|
|
|
|
|
|
-class PaymentAdmin(admin.ModelAdmin):
|
|
|
- list_display = ('id', 'type_verbose', 'get_adhesion', 'amount', 'period', 'start_display', 'end_display',)
|
|
|
-# #list_display_links = None
|
|
|
- list_filter = (PaymentTypeFilter, 'payment_method', 'validated',)
|
|
|
-#
|
|
|
-# def get_list_display(self, request):
|
|
|
-# list_display = ()
|
|
|
-# #if request.user.has_perm('auth.change_user'):
|
|
|
-# # list_display += ('adherent_link',)
|
|
|
-# #else:
|
|
|
-# #list_display += ('get_adherent',)
|
|
|
-# list_display += ('payment_type_verbose', 'get_adherent', 'amount',
|
|
|
-# 'period', 'payment_method', 'start_display', 'end_display', 'validated_display',)
|
|
|
-# #if request.user.has_perm('banking.validate_payment'):
|
|
|
-# # list_display += ('change',)
|
|
|
-# #else:
|
|
|
-# # list_display += ('change_pending',)
|
|
|
-# return list_display
|
|
|
-#
|
|
|
- def start_display(self, obj):
|
|
|
- return obj.start
|
|
|
- start_display.short_description = 'Début'
|
|
|
-
|
|
|
- def end_display(self, obj):
|
|
|
- return obj.end
|
|
|
- end_display.short_description = 'Fin'
|
|
|
-#
|
|
|
-# def validated_display(self, obj):
|
|
|
-# return obj.validated
|
|
|
-# validated_display.short_description = 'Validé'
|
|
|
-# validated_display.boolean = True
|
|
|
-#
|
|
|
-# #def adherent_link(self, obj):
|
|
|
-# # adherent = obj.get_adherent()
|
|
|
-# # url = reverse('admin:adhesions_adherent_change', args=[adherent.pk])
|
|
|
-# # return '<a href="%s">%s</a>' % (url, adherent)
|
|
|
-# #adherent_link.short_description = 'Adhérent'
|
|
|
-# #adherent_link.allow_tags = True
|
|
|
-#
|
|
|
-# def change(self, obj):
|
|
|
-# url = reverse('admin:banking_payment_change', args=[obj.pk])
|
|
|
-# return '<a href="%s" class="changelink">Modifier</a>' % url
|
|
|
-# change.short_description = ''
|
|
|
-# change.allow_tags = True
|
|
|
-#
|
|
|
-# def change_pending(self, obj):
|
|
|
-# if obj.validated:
|
|
|
-# return '-'
|
|
|
-# else:
|
|
|
-# return self.change(obj)
|
|
|
-# change_pending.short_description = ''
|
|
|
-# change_pending.allow_tags = True
|
|
|
-#
|
|
|
-# def get_actions(self, request):
|
|
|
-# actions = super().get_actions(request)
|
|
|
-# if request.user.has_perm('banking.validate_payment'):
|
|
|
-# actions['validate'] = (validate_payment, 'validate', 'Valider les paiements sélectionnés')
|
|
|
-# return actions
|
|
|
-#
|
|
|
-# def has_add_permission(self, request):
|
|
|
-# return False
|
|
|
-#
|
|
|
-# def has_change_permission(self, request, obj=None):
|
|
|
-# if obj and not request.user.has_perm('banking.validate_payment'):
|
|
|
-# return not obj.validated
|
|
|
-# return True
|
|
|
-#
|
|
|
-# def has_delete_permission(self, request, obj=None):
|
|
|
-# if obj and not request.user.has_perm('banking.validate_payment'):
|
|
|
-# return not obj.validated
|
|
|
-# return False
|
|
|
-
|
|
|
-
|
|
|
-#admin.site.register(Payment, PaymentAdmin)
|
|
|
+class RecurringPaymentAdmin(admin.ModelAdmin):
|
|
|
+ list_display = ('id', 'payment_type', 'payment_object_link', 'get_status', 'get_last_validated_update', 'get_pending',)
|
|
|
+ list_select_related = ('adhesion', 'service', 'service__service_type',)
|
|
|
+ inlines = (PendingPaymentUpdateInline, ValidatedPaymentUpdateInline,)
|
|
|
+ list_filter = (PaymentTypeFilter, PaymentStatusFilter, PendingPaymentFilter,)
|
|
|
+ fields = ('payment_type', 'payment_object_link',)
|
|
|
+ readonly_fields = ('payment_type', 'payment_object_link',)
|
|
|
+ search_fields = \
|
|
|
+ tuple([prefix_search_field('adhesion', f) for f in AdhesionAdmin.search_fields]) \
|
|
|
+ + tuple([prefix_search_field('service', f) for f in ServiceAdmin.search_fields])
|
|
|
+
|
|
|
+ def get_queryset(self, request):
|
|
|
+ qs = super().get_queryset(request)
|
|
|
+ qs = qs.prefetch_related('updates')
|
|
|
+ qs = qs.prefetch_related(
|
|
|
+ models.Prefetch(
|
|
|
+ 'updates',
|
|
|
+ queryset=PaymentUpdate.objects.filter(validated=False),
|
|
|
+ to_attr='pending_updates'
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return qs
|
|
|
+
|
|
|
+ def get_pending(self, obj):
|
|
|
+ return len(obj.pending_updates)
|
|
|
+ get_pending.short_description = 'Opérations en attente'
|
|
|
+
|
|
|
+ def payment_object_link(self, obj):
|
|
|
+ obj = obj.payment_object()
|
|
|
+ return format_html(u'<a href="{}">{}</a>', obj.get_absolute_url(), obj)
|
|
|
+ payment_object_link.short_description = 'Objet'
|
|
|
+
|
|
|
+ def get_actions(self, request):
|
|
|
+ actions = super().get_actions(request)
|
|
|
+ if 'delete_selected' in actions:
|
|
|
+ del actions['delete_selected']
|
|
|
+ return actions
|
|
|
+
|
|
|
+ def has_add_permission(self, request, obj=None):
|
|
|
+ return False
|
|
|
+
|
|
|
+ def has_delete_permission(self, request, obj=None):
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+admin.site.register(RecurringPayment, RecurringPaymentAdmin)
|