123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- # -*- coding: utf-8 -*-
- from __future__ import unicode_literals
- from datetime import date
- from django.shortcuts import get_object_or_404
- from django.contrib import admin
- from django.contrib import messages
- from django.contrib.admin import SimpleListFilter
- from django.contrib.auth.admin import UserAdmin
- from django.contrib.contenttypes.models import ContentType
- from django.http import HttpResponseRedirect
- from django.conf.urls import url
- from django.conf import settings
- from django.core.urlresolvers import reverse
- from django.utils.safestring import mark_safe
- from coin.members.models import (
- Member, CryptoKey, LdapUser, MembershipFee, Offer, OfferSubscription, RowLevelPermission)
- from coin.members.forms import AdminMemberChangeForm, MemberCreationForm
- from coin.utils import delete_selected
- class CryptoKeyInline(admin.StackedInline):
- model = CryptoKey
- extra = 0
- class MembershipFeeInline(admin.TabularInline):
- model = MembershipFee
- extra = 0
- fields = ('start_date', 'end_date', 'amount', 'payment_method',
- 'reference', 'payment_date')
- class OfferSubscriptionInline(admin.TabularInline):
- model = OfferSubscription
- extra = 0
- writable_fields = ('subscription_date', 'resign_date', 'commitment', 'offer')
- all_fields = ('get_subscription_reference',) + writable_fields
- def get_fields(self, request, obj=None):
- if obj:
- return self.all_fields
- else:
- return self.writable_fields
- def get_readonly_fields(self, request, obj=None):
- # création ou superuser : lecture écriture
- if not obj or request.user.is_superuser:
- return ('get_subscription_reference',)
- # modification : lecture seule seulement
- else:
- return self.all_fields
- show_change_link = True
- def formfield_for_foreignkey(self, db_field, request, **kwargs):
- if request.user.is_superuser:
- return super(OfferSubscriptionInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
- else:
- if db_field.name == "offer":
- kwargs["queryset"] = Offer.objects.manageable_by(request.user)
- return super(OfferSubscriptionInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
- def has_add_permission(self, request):
- # - Quand on *crée* un membre on autorise à ajouter un abonnement
- # - Quand on *édite* un membre, on interdit l'ajout d'abonnements (sauf
- # par le bureau) car cela permettrait de gagner à loisir accès à
- # toute fiche adhérent en lui ajoutant un abonnement à une offre dont
- # on a la gestion).
- return (
- request.resolver_match.view_name == 'admin:members_member_add'
- or
- request.user.is_superuser
- )
- # sinon on pourrait supprimer les abo qu'on ne peut pas gérer
- # pourrait peut-être être plus fin, obj réfère ici au member de la page
- def has_delete_permission(self, request, obj=None):
- return request.user.is_superuser
- class DataRetentionFilter(SimpleListFilter):
- # Human-readable title which will be displayed in the
- # right admin sidebar just above the filter options.
- title = 'péremption des données'
- # Parameter for the filter that will be used in the URL query.
- parameter_name = 'data_cleanup'
- def lookups(self, request, model_admin):
- return (
- ('pending_deletion', 'Pouvant légalement être supprimé'),
- )
- def queryset(self, request, queryset):
- if self.value() == 'pending_deletion':
- return queryset.could_be_deleted()
- class MembershipFeeFilter(SimpleListFilter):
- # Human-readable title which will be displayed in the
- # right admin sidebar just above the filter options.
- title = 'Cotisations'
- # Parameter for the filter that will be used in the URL query.
- parameter_name = 'fee'
- def lookups(self, request, model_admin):
- return (
- ('paidup', 'À jour de cotisation'),
- ('late', 'En retard'),
- )
- def queryset(self, request, queryset):
- if self.value() == 'paidup':
- return queryset.paidup_fee()
- if self.value() == 'late':
- return queryset.no_fee_or_late()
- class MemberAdmin(UserAdmin):
- SERVICE_NO_FEE_MSG = (
- "Cet·te adhérent·e bénéficie d'un service"
- " mais n'est pas à jour de cotisation.")
- RECENT_SERVICE_MSG = (
- "Cet·te ancien·ne adhérent·e a un service résilié depuis moins d'un an"
- ". Légalement, il faut conserver sa fiche un an après la résiliation "
- "du service.")
- list_display = ('id', 'status', 'username', 'first_name',
- 'name_or_organization_name',
- 'nickname', 'email',
- 'enhanced_end_date_of_membership')
- list_display_links = ('id', 'username', 'first_name', 'name_or_organization_name')
- list_filter = ('status', MembershipFeeFilter, DataRetentionFilter)
- search_fields = ['username', 'first_name', 'last_name', 'email', 'nickname']
- ordering = ('status', 'username')
- actions = [delete_selected, 'set_as_member', 'set_as_non_member',
- 'bulk_send_welcome_email', 'bulk_send_call_for_membership_fee_email']
- form = AdminMemberChangeForm
- add_form = MemberCreationForm
- def name_or_organization_name(self, obj):
- """ Used to spare some horizontal space in list view"""
- return obj.organization_name or obj.last_name
- name_or_organization_name.short_description = 'Nom'
- def enhanced_end_date_of_membership(self, obj):
- membership_end = obj.end_date_of_membership()
- active_subscriptions = obj.get_active_subscriptions()
- recent_inactive_subscriptions = obj.get_recent_inactive_subscriptions()
- note = ''
- tooltip = ''
- if (membership_end is not None and membership_end < date.today()
- and recent_inactive_subscriptions.exists() and not active_subscriptions.exists()):
- note = ' <i class="fa fa-question-circle" style="color: #cccc40"></i>'
- tooltip = self.RECENT_SERVICE_MSG
- if (active_subscriptions.exists() and (
- membership_end is None or membership_end <= date.today())):
- note = ' <i class="fa fa-question-circle" style="color: red"></i>'
- tooltip = self.SERVICE_NO_FEE_MSG
- out = '{}{}'.format(
- obj.end_date_of_membership() or "pas de cotisation", note)
- if note:
- return mark_safe('<span style="cursor: help;" title="{}">{}</span>'.format(tooltip, out))
- else:
- return mark_safe(out)
- return mark_safe()
- enhanced_end_date_of_membership.short_description = "Date de fin d'adhésion"
- def get_fieldsets(self, request, obj=None):
- coord_fieldset = ('Coordonnées', {'fields': (
- ('email', 'send_membership_fees_email'),
- ('home_phone_number', 'mobile_phone_number'),
- 'address',
- ('postal_code', 'city', 'country'))})
- auth_fieldset = ('Authentification', {'fields': (
- ('username', 'password'))})
- perm_fieldset = ('Permissions', {'fields': (
- ('is_active', 'is_staff', 'is_superuser', 'groups'))})
- # if obj is null then it is a creation, otherwise it is a modification
- if obj:
- fieldsets = (
- ('Adhérent', {'fields': (
- ('status', 'date_joined', 'resign_date'),
- 'type',
- ('first_name', 'last_name', 'nickname'),
- 'organization_name',
- 'comments'
- )}),
- coord_fieldset,
- auth_fieldset,
- perm_fieldset,
- (None, {'fields': ('date_last_call_for_membership_fees_email',)})
- )
- else:
- fieldsets = (
- ('Adhérent', {'fields': (
- ('status', 'date_joined'),
- 'type',
- ('first_name', 'last_name', 'nickname'),
- 'organization_name',
- 'comments')}),
- coord_fieldset,
- auth_fieldset,
- perm_fieldset
- )
- if settings.HANDLE_BALANCE:
- fieldsets[0][1]['fields'] += ('balance',)
- return fieldsets
- radio_fields = {"type": admin.HORIZONTAL}
- save_on_top = True
- inlines = [CryptoKeyInline, MembershipFeeInline, OfferSubscriptionInline]
- def add_member_warnings(self, request, member):
- has_active_subscriptions = member.get_active_subscriptions().exists()
- has_recent_resigned_subscriptions = member.get_recent_inactive_subscriptions()
- if not member.is_paid_up() and has_active_subscriptions:
- messages.error(request, self.SERVICE_NO_FEE_MSG)
- elif not has_active_subscriptions and has_recent_resigned_subscriptions:
- messages.warning(request, self.RECENT_SERVICE_MSG)
- def get_form(self, request, obj=None, *args, **kwargs):
- if obj:
- self.add_member_warnings(request, obj)
- return super(MemberAdmin, self).get_form(request, obj, *args, **kwargs)
- def get_queryset(self, request):
- qs = super(MemberAdmin, self).get_queryset(request)
- if request.user.is_superuser:
- return qs
- else:
- offers = Offer.objects.manageable_by(request.user)
- return qs.filter(offersubscription__offer__in=offers).distinct()
- def get_readonly_fields(self, request, obj=None):
- readonly_fields = []
- if obj:
- # Remove help_text for readonly field (can't do that in the Form
- # django seems to user help_text from model for readonly fields)
- username_field = [
- f for f in obj._meta.fields if f.name == 'username']
- username_field[0].help_text = ''
- readonly_fields.append('username')
- if not request.user.is_superuser:
- readonly_fields += ['is_active', 'is_staff', 'is_superuser', 'groups', 'date_last_call_for_membership_fees_email']
- return readonly_fields
- def set_as_member(self, request, queryset):
- rows_updated = queryset.update(status='member')
- self.message_user(
- request,
- '%d membre(s) définis comme adhérent(s).' % rows_updated)
- set_as_member.short_description = 'Définir comme adhérent'
- def set_as_non_member(self, request, queryset):
- rows_updated = queryset.update(status='not_member')
- self.message_user(
- request,
- '%d membre(s) définis comme non adhérent(s).' % rows_updated)
- set_as_non_member.short_description = "Définir comme non adhérent"
- def get_urls(self):
- """Custom admin urls"""
- urls = super(MemberAdmin, self).get_urls()
- my_urls = [
- url(r'^send_welcome_email/(?P<id>\d+)$',
- self.admin_site.admin_view(self.send_welcome_email),
- name='send_welcome_email'),
- ]
- return my_urls + urls
- def send_welcome_email(self, request, id, return_httpredirect=True):
- """
- Vue appelée lorsque l'admin souhaite envoyer l'email de bienvenue à un
- membre.
- """
- # TODO : Add better perm here
- if request.user.is_superuser:
- member = get_object_or_404(Member, pk=id)
- member.send_welcome_email()
- messages.success(request,
- 'Le courriel de bienvenue a été envoyé à %s' % member.email)
- else:
- messages.error(
- request, 'Vous n\'avez pas l\'autorisation d\'envoyer des '
- 'courriels de bienvenue.')
- if return_httpredirect:
- return HttpResponseRedirect(reverse('admin:members_member_changelist'))
- def bulk_send_welcome_email(self, request, queryset):
- """
- Action appelée lorsque l'admin souhaite envoyer un lot d'email de bienvenue
- depuis une sélection de membre dans la vue liste de l'admin
- """
- for member in queryset.all():
- self.send_welcome_email(
- request, member.id, return_httpredirect=False)
- messages.success(request,
- 'Le courriel de bienvenue a été envoyé à %d membre(s).' % queryset.count())
- bulk_send_welcome_email.short_description = "Envoyer le courriel de bienvenue"
- def bulk_send_call_for_membership_fee_email(self, request, queryset):
- # TODO : Add better perm here
- if not request.user.is_superuser:
- messages.error(
- request, 'Vous n\'avez pas l\'autorisation d\'envoyer des '
- 'courriels de relance.')
- return
- cpt_success = 0
- for member in queryset.all():
- if member.send_call_for_membership_fees_email():
- cpt_success += 1
- else:
- messages.warning(request,
- "Le courriel de relance de cotisation n\'a pas "
- "été envoyé à {member} ({email}) car il a déjà "
- "reçu une relance le {last_call_date}"\
- .format(member=member,
- email=member.email,
- last_call_date=member.date_last_call_for_membership_fees_email))
- if cpt_success == 1:
- member = queryset.first()
- messages.success(request,
- "Le courriel de relance de cotisation a été "
- "envoyé à {member} ({email})"\
- .format(member=member, email=member.email))
- elif cpt_success > 1:
- messages.success(request,
- "Le courriel de relance de cotisation a été "
- "envoyé à {cpt} membres"\
- .format(cpt=cpt_success))
- bulk_send_call_for_membership_fee_email.short_description = 'Envoyer le courriel de relance de cotisation'
- class RowLevelPermissionAdmin(admin.ModelAdmin):
- def get_changeform_initial_data(self, request):
- return {'content_type': ContentType.objects.get_for_model(OfferSubscription)}
- admin.site.register(Member, MemberAdmin)
- # admin.site.unregister(Group)
- # admin.site.register(LdapUser, LdapUserAdmin)
- admin.site.register(RowLevelPermission, RowLevelPermissionAdmin)
|