admin.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from django.contrib import admin
  4. from django.contrib import messages
  5. from django.http import HttpResponseRedirect
  6. from django.conf.urls import url
  7. from django.contrib.admin.utils import flatten_fieldsets
  8. from django import forms
  9. from coin.filtering_queryset import LimitedAdminInlineMixin
  10. from coin.billing.models import Invoice, InvoiceDetail, Payment, PaymentAllocation
  11. from coin.billing.utils import get_invoice_from_id_or_number
  12. from django.core.urlresolvers import reverse
  13. import autocomplete_light
  14. class InvoiceDetailInlineForm(forms.ModelForm):
  15. class Meta:
  16. model = InvoiceDetail
  17. fields = (
  18. 'label', 'amount', 'quantity', 'tax',
  19. 'offersubscription', 'period_from', 'period_to'
  20. )
  21. widgets = {'quantity': forms.NumberInput(attrs={'step': 1})}
  22. class InvoiceDetailInline(LimitedAdminInlineMixin, admin.StackedInline):
  23. model = InvoiceDetail
  24. extra = 0
  25. fields = (('label', 'amount', 'quantity', 'tax'),
  26. ('offersubscription', 'period_from', 'period_to'))
  27. form = InvoiceDetailInlineForm
  28. def get_filters(self, obj):
  29. """
  30. Le champ "Abonnement" est filtré afin de n'afficher que les abonnements
  31. du membre choisi dans la facture. Si pas de membre alors renvoi
  32. une liste vide
  33. """
  34. if obj and obj.member:
  35. return (('offersubscription', {'member': obj.member}),)
  36. else:
  37. return (('offersubscription', None),)
  38. def get_readonly_fields(self, request, obj=None):
  39. if not obj or not obj.member:
  40. return self.readonly_fields + ('offersubscription',)
  41. return self.readonly_fields
  42. class InvoiceDetailInlineReadOnly(admin.StackedInline):
  43. """
  44. Lorsque la facture est validée, il n'est plus possible de la modifier
  45. Ce inline est donc identique à InvoiceDetailInline, mais tous
  46. les champs sont en lecture seule
  47. """
  48. model = InvoiceDetail
  49. extra = 0
  50. fields = InvoiceDetailInline.fields
  51. can_delete = False
  52. def has_add_permission(self, request):
  53. return False
  54. def get_readonly_fields(self, request, obj=None):
  55. if self.declared_fieldsets:
  56. result = flatten_fieldsets(self.declared_fieldsets)
  57. else:
  58. result = list(set(
  59. [field.name for field in self.opts.local_fields] +
  60. [field.name for field in self.opts.local_many_to_many]
  61. ))
  62. result.remove('id')
  63. return result
  64. class PaymentAllocatedReadOnly(admin.TabularInline):
  65. model = PaymentAllocation
  66. extra = 0
  67. fields = ("payment", "amount")
  68. readonly_fields = ("payment", "amount")
  69. verbose_name = None
  70. verbose_name_plural = "Paiement alloués"
  71. def has_add_permission(self, request, obj=None):
  72. return False
  73. def has_delete_permission(self, request, obj=None):
  74. return False
  75. class PaymentInlineAdd(admin.StackedInline):
  76. model = Payment
  77. extra = 0
  78. fields = (('date', 'payment_mean', 'amount'),)
  79. can_delete = False
  80. verbose_name_plural = "Ajouter des paiements"
  81. def has_change_permission(self, request):
  82. return False
  83. class InvoiceAdmin(admin.ModelAdmin):
  84. list_display_links = ('number', 'date')
  85. list_filter = ('status', 'validated')
  86. search_fields = ['member__username', 'member__first_name', 'member__last_name', 'member__email', 'member__nickname', 'number', 'date']
  87. list_display = ('number', 'date', 'status', 'amount', 'member',
  88. 'validated')
  89. fields = (('number', 'date', 'status'),
  90. ('date_due'),
  91. ('member'),
  92. ('amount', 'amount_paid'),
  93. ('validated', 'pdf'))
  94. readonly_fields = ('amount', 'amount_paid', 'validated', 'pdf', 'number')
  95. form = autocomplete_light.modelform_factory(Invoice, fields='__all__')
  96. def get_readonly_fields(self, request, obj=None):
  97. """
  98. Si la facture est validée, passe tous les champs en readonly
  99. """
  100. if obj and obj.validated:
  101. if self.declared_fieldsets:
  102. return flatten_fieldsets(self.declared_fieldsets)
  103. else:
  104. return list(set(
  105. [field.name for field in self.opts.local_fields] +
  106. [field.name for field in self.opts.local_many_to_many]
  107. ))
  108. return self.readonly_fields
  109. def get_inline_instances(self, request, obj=None):
  110. """
  111. Renvoi les inlines selon le context :
  112. * Si création, alors ne renvoi aucun inline
  113. * Si modification, renvoi InvoiceDetail et PaymentInline
  114. * Si facture validée, renvoi InvoiceDetail en ReadOnly et PaymentInline
  115. """
  116. inlines = []
  117. inline_instances = []
  118. if obj is not None:
  119. if obj.validated:
  120. inlines = [InvoiceDetailInlineReadOnly]
  121. else:
  122. inlines = [InvoiceDetailInline]
  123. if obj.validated:
  124. inlines += [PaymentAllocatedReadOnly]
  125. if obj.status == "open":
  126. inlines += [PaymentInlineAdd]
  127. for inline_class in inlines:
  128. inline = inline_class(self.model, self.admin_site)
  129. if request:
  130. if not (inline.has_add_permission(request) or
  131. inline.has_change_permission(request) or
  132. inline.has_delete_permission(request)):
  133. continue
  134. if not inline.has_add_permission(request):
  135. inline.max_num = 0
  136. inline_instances.append(inline)
  137. return inline_instances
  138. def get_urls(self):
  139. """
  140. Custom admin urls
  141. """
  142. urls = super(InvoiceAdmin, self).get_urls()
  143. my_urls = [
  144. url(r'^validate/(?P<id>.+)$',
  145. self.admin_site.admin_view(self.validate_view),
  146. name='invoice_validate'),
  147. ]
  148. return my_urls + urls
  149. def validate_view(self, request, id):
  150. """
  151. Vue appelée lorsque l'admin souhaite valider une facture et
  152. générer son pdf
  153. """
  154. # TODO : Add better perm here
  155. if request.user.is_superuser:
  156. invoice = get_invoice_from_id_or_number(id)
  157. if invoice.amount() == 0:
  158. messages.error(request, 'Une facture validée ne peut pas avoir'
  159. ' un total de 0€.')
  160. else:
  161. invoice.validate()
  162. messages.success(request, 'La facture a été validée.')
  163. else:
  164. messages.error(
  165. request, 'Vous n\'avez pas l\'autorisation de valider '
  166. 'une facture.')
  167. return HttpResponseRedirect(reverse('admin:billing_invoice_change',
  168. args=(id,)))
  169. class PaymentAllocationInlineReadOnly(admin.TabularInline):
  170. model = PaymentAllocation
  171. extra = 0
  172. fields = ("invoice", "amount")
  173. readonly_fields = ("invoice", "amount")
  174. verbose_name = None
  175. verbose_name_plural = "Alloué à"
  176. def has_add_permission(self, request, obj=None):
  177. return False
  178. def has_delete_permission(self, request, obj=None):
  179. return False
  180. class PaymentAdmin(admin.ModelAdmin):
  181. list_display = ('__unicode__', 'member', 'payment_mean', 'amount', 'date',
  182. 'amount_already_allocated', 'label')
  183. list_display_links = ()
  184. fields = (('member'),
  185. ('amount', 'payment_mean', 'date', 'label'),
  186. ('amount_already_allocated'))
  187. readonly_fields = ('amount_already_allocated', 'label')
  188. form = autocomplete_light.modelform_factory(Payment, fields='__all__')
  189. def get_readonly_fields(self, request, obj=None):
  190. # If payment already started to be allocated or already have a member
  191. if obj and (obj.amount_already_allocated() != 0 or obj.member != None):
  192. # All fields are readonly
  193. return flatten_fieldsets(self.declared_fieldsets)
  194. else:
  195. return self.readonly_fields
  196. def get_inline_instances(self, request, obj=None):
  197. return [PaymentAllocationInlineReadOnly(self.model, self.admin_site)]
  198. admin.site.register(Invoice, InvoiceAdmin)
  199. admin.site.register(Payment, PaymentAdmin)