admin.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. from django.contrib import admin
  2. from django.db import models
  3. from django.urls import reverse
  4. from django.forms import BaseInlineFormSet
  5. from django.utils.html import format_html
  6. from django.conf.urls import url
  7. from django.shortcuts import get_object_or_404
  8. from django.template.response import TemplateResponse
  9. from functools import update_wrapper
  10. from services.models import ServiceType
  11. from services.admin import ServiceAdmin
  12. from adhesions.admin import AdhesionAdmin
  13. from djadhere.utils import ActiveFilter
  14. from .models import RecurringPayment, PaymentUpdate
  15. from .utils import notify_payment_update
  16. ### Inlines
  17. class PaymentMethodFilter(admin.SimpleListFilter):
  18. title = 'méthode de paiement'
  19. parameter_name = 'method'
  20. def lookups(self, request, model_admin):
  21. return PaymentUpdate.PAYMENT_CHOICES
  22. def queryset(self, request, queryset):
  23. if self.value() is not None:
  24. return queryset.filter(payment_method=self.value())
  25. else:
  26. return queryset.exclude(payment_method=PaymentUpdate.STOP)
  27. class PendingPaymentFilter(admin.SimpleListFilter):
  28. title = 'statut'
  29. parameter_name = 'status'
  30. def lookups(self, request, model_admin):
  31. return (
  32. ('up-to-date', 'À jour'),
  33. ('pending', 'En attente'),
  34. )
  35. def queryset(self, request, queryset):
  36. if self.value() == 'up-to-date':
  37. queryset = queryset.filter(last=True)
  38. if self.value() == 'pending':
  39. queryset = queryset.filter(last=False)
  40. return queryset
  41. payments = RecurringPayment.objects.annotate(
  42. validated=models.Subquery(
  43. PaymentUpdate.objects.filter(payment=models.OuterRef('pk'))
  44. .filter(validated=True)
  45. .order_by('-start')
  46. .values('pk')[:1]
  47. ),
  48. pending=models.Subquery(
  49. PaymentUpdate.objects.filter(payment=models.OuterRef('pk'))
  50. .filter(validated=False)
  51. .order_by('-start')
  52. .values('pk')[:1]
  53. ),
  54. )
  55. up_to_date = payments.filter(pending__isnull=True)
  56. pending = payments.exclude(pending__isnull=True)
  57. if self.value() == 'up-to-date':
  58. p = up_to_date
  59. elif self.value() == 'pending':
  60. p = pending
  61. else:
  62. p = payments
  63. queryset = queryset.filter(pk__in=p.values_list('validated', flat=True))
  64. queryset = queryset.annotate(
  65. last=models.Case(
  66. models.When(pk__in=up_to_date.values_list('validated', flat=True), then=True),
  67. default=False,
  68. output_field=models.BooleanField(),
  69. ),
  70. )
  71. return queryset
  72. class PaymentTypeFilter(admin.SimpleListFilter):
  73. title = 'type'
  74. parameter_name = 'type'
  75. def lookups(self, request, model_admin):
  76. return (
  77. ('adhesion', 'Adhésion'),
  78. ('service', 'Service'),
  79. )
  80. def queryset(self, request, queryset):
  81. if self.value() == 'adhesion':
  82. return queryset.filter(payment__adhesion__isnull=False)
  83. if self.value() == 'service':
  84. return queryset.filter(payment__service__isnull=False)
  85. ### Inlines
  86. class PendingPaymentUpdateFormSet(BaseInlineFormSet):
  87. def save_new(self, form, commit=True):
  88. obj = super().save_new(form, commit)
  89. if not obj.validated:
  90. notify_payment_update(self.request, obj)
  91. return obj
  92. def save_existing(self, form, instance, commit=True):
  93. old = PaymentUpdate.objects.get(pk=instance.pk)
  94. if not instance.validated:
  95. notify_payment_update(self.request, instance, old)
  96. return super().save_existing(form, instance, commit)
  97. class PendingPaymentUpdateInline(admin.TabularInline):
  98. model = PaymentUpdate
  99. formset = PendingPaymentUpdateFormSet
  100. extra = 1
  101. max_num = 1
  102. verbose_name_plural = 'Demande de saisie bancaire'
  103. def get_formset(self, request, obj=None, **kwargs):
  104. formset = super().get_formset(request, obj, **kwargs)
  105. formset.request = request
  106. return formset
  107. def get_queryset(self, request):
  108. return super().get_queryset(request).filter(validated=False)
  109. class ValidatedPaymentUpdateInline(admin.TabularInline):
  110. model = PaymentUpdate
  111. verbose_name_plural = 'Historique'
  112. max_num = 0
  113. fields = ('amount', 'period', 'payment_method', 'start',)
  114. readonly_fields = ('amount', 'period', 'payment_method', 'start',)
  115. def has_delete_permission(self, request, obj=None):
  116. return False
  117. def get_queryset(self, request):
  118. return super().get_queryset(request).filter(validated=True)
  119. ### Helpers
  120. def prefix_search_field(prefix, field):
  121. if field[0] == '=':
  122. return '=' + prefix + '__' + field[1:]
  123. else:
  124. return prefix + '__' + field
  125. ### ModelAdmin
  126. class RecurringPaymentAdmin(admin.ModelAdmin):
  127. inlines = (PendingPaymentUpdateInline, ValidatedPaymentUpdateInline,)
  128. fields = ('payment_type', 'payment_object_link', 'debtor_link',)
  129. readonly_fields = ('payment_type', 'payment_object_link', 'debtor_link',)
  130. def payment_object_link(self, obj):
  131. obj = obj.payment_object()
  132. return format_html(u'<a href="{}">{}</a>', obj.get_absolute_url(), obj)
  133. payment_object_link.short_description = 'Objet'
  134. def debtor_link(self, obj):
  135. url = reverse(viewname='admin:%s_%s_debtor' % (obj._meta.app_label, obj._meta.model_name), args=[obj.pk])
  136. return format_html(u'<a href="{}">{}</a>', url, obj.debtor)
  137. debtor_link.short_description = 'Débiteur'
  138. def get_actions(self, request):
  139. actions = super().get_actions(request)
  140. if 'delete_selected' in actions:
  141. del actions['delete_selected']
  142. return actions
  143. def has_add_permission(self, request, obj=None):
  144. return False
  145. def has_change_permission(self, request, obj=None):
  146. return obj
  147. def has_delete_permission(self, request, obj=None):
  148. return False
  149. def get_urls(self):
  150. info = self.model._meta.app_label, self.model._meta.model_name
  151. urls = [
  152. url(r'^(.*)/debtor/$', self.admin_site.admin_view(self.debtor_view), name='%s_%s_debtor' % info),
  153. ]
  154. return urls + super().get_urls()
  155. def debtor_view(self, request, payment_pk):
  156. payment = get_object_or_404(RecurringPayment, pk=payment_pk)
  157. adhesion = payment.debtor
  158. if adhesion.is_physical():
  159. profile = adhesion.user.profile
  160. else:
  161. profile = adhesion.corporation
  162. context = dict(
  163. self.admin_site.each_context(request),
  164. opts=self.model._meta,
  165. payment=payment,
  166. adhesion=adhesion,
  167. adherent=adhesion.adherent,
  168. profile=profile,
  169. )
  170. return TemplateResponse(request, 'banking/debtor.html', context)
  171. class PaymentUpdateAdmin(admin.ModelAdmin):
  172. list_display = ('payment_type', 'payment_object_link', 'payment_link', 'last',)
  173. list_select_related = ('payment', 'payment__adhesion', 'payment__service', 'payment__service__service_type',)
  174. list_filter = (PaymentTypeFilter, PaymentMethodFilter, PendingPaymentFilter,)
  175. list_display_links = None
  176. search_fields = \
  177. tuple([prefix_search_field('payment__adhesion', f) for f in AdhesionAdmin.search_fields]) \
  178. + tuple([prefix_search_field('payment__service', f) for f in ServiceAdmin.search_fields])
  179. def last(self, obj):
  180. return obj.last
  181. last.boolean = True
  182. last.short_description = 'À jour'
  183. def payment_type(self, update):
  184. return update.payment.payment_type()
  185. payment_type.short_description = 'Type'
  186. def payment_object_link(self, update):
  187. obj = update.payment.payment_object()
  188. return format_html(u'<a href="{}">{}</a>', obj.get_absolute_url(), obj)
  189. payment_object_link.short_description = 'Objet'
  190. def payment_link(self, update):
  191. payment = update.payment
  192. return format_html(u'<a href="{}">{}</a>', payment.get_absolute_url(), update)
  193. payment_link.short_description = 'Paiement'
  194. def get_queryset(self, request):
  195. qs = super().get_queryset(request)
  196. payments = RecurringPayment.objects.annotate(
  197. validated=models.Subquery(
  198. PaymentUpdate.objects.filter(payment=models.OuterRef('pk'))
  199. .filter(validated=True)
  200. .order_by('-start')
  201. .values('pk')[:1]
  202. ),
  203. pending=models.Subquery(
  204. PaymentUpdate.objects.filter(payment=models.OuterRef('pk'))
  205. .filter(validated=False)
  206. .order_by('-start')
  207. .values('pk')[:1]
  208. ),
  209. )
  210. qs = qs.filter(pk__in=payments.values_list('validated', flat=True))
  211. qs = qs.annotate(
  212. last=models.Case(
  213. models.When(pk__in=payments.filter(pending__isnull=True).values_list('validated', flat=True), then=True),
  214. default=False,
  215. output_field=models.BooleanField(),
  216. ),
  217. )
  218. return qs
  219. def get_actions(self, request):
  220. actions = super().get_actions(request)
  221. if 'delete_selected' in actions:
  222. del actions['delete_selected']
  223. return actions
  224. def has_add_permission(self, request, obj=None):
  225. return False
  226. def has_change_permission(self, request, obj=None):
  227. return not obj
  228. def has_delete_permission(self, request, obj=None):
  229. return False
  230. admin.site.register(RecurringPayment, RecurringPaymentAdmin)
  231. admin.site.register(PaymentUpdate, PaymentUpdateAdmin)