Browse Source

Allow to filter members that could be deleted

In order to bulk-delete them easily then

Fix #137 Ref #30
Jocelyn Delalande 6 years ago
parent
commit
5eb2720d5c
4 changed files with 80 additions and 1 deletions
  1. 19 1
      coin/members/admin.py
  2. 10 0
      coin/members/models.py
  3. 48 0
      coin/members/tests.py
  4. 3 0
      coin/utils.py

+ 19 - 1
coin/members/admin.py

@@ -83,6 +83,24 @@ class OfferSubscriptionInline(admin.TabularInline):
         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.
@@ -110,7 +128,7 @@ class MemberAdmin(UserAdmin):
                     'nickname', 'email',
                     'enhanced_end_date_of_membership')
     list_display_links = ('id', 'username', 'first_name', 'name_or_organization_name')
-    list_filter = ('status', MembershipFeeFilter)
+    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',

+ 10 - 0
coin/members/models.py

@@ -35,6 +35,16 @@ class MemberQuerySet(models.QuerySet):
     def no_fee_or_late(self):
         return self.exclude(self.paidup_q)
 
+    def could_be_deleted(self):
+        return self.exclude(
+            # we have at least one subscription
+            Q(offersubscription__isnull=False),
+            # still running or  resigned less than one year ago
+            Q(offersubscription__resign_date__isnull=True)
+            |
+            Q(offersubscription__resign_date__gte=utils.one_year_ago())
+        ).exclude(self.paidup_q)
+
 
 class MemberManager(UserManager):
     use_in_migrations = False

+ 48 - 0
coin/members/tests.py

@@ -9,6 +9,7 @@ from cStringIO import StringIO
 from dateutil.relativedelta import relativedelta
 from freezegun import freeze_time
 import unittest
+from freezegun import freeze_time
 
 from django import db
 from django.conf import settings
@@ -17,6 +18,7 @@ from django.core import mail, management
 from django.core.exceptions import ValidationError
 
 from coin.members.models import Member, MembershipFee, LdapUser
+from coin.offers.models import OfferSubscription, Offer
 from coin.validation import chatroom_url_validator
 
 
@@ -509,6 +511,52 @@ class MemberTestCallForMembershipCommand(TestCase):
         self.do_test_email_sent(0)
 
 
+class MemberManagerTest(TestCase):
+    def setUp(self):
+        self.ab = Member.objects.create(
+            first_name='a', last_name='b', username='ab', email='ab@ex.com')
+        self.cd = Member.objects.create(
+            first_name='c', last_name='d', username='cd', email='cd@ex.com')
+        self.ef = Member.objects.create(
+            first_name='e', last_name='f', username='ef', email='ef@ex.com')
+        self.gh = Member.objects.create(
+            first_name='g', last_name='h', username='gh', email='gh@ex.com')
+
+        MembershipFee.objects.create(member=self.ab, amount=20,
+                      start_date=date(2015, 11, 11),
+                      end_date=date(2016, 11, 11))
+
+        MembershipFee.objects.create(member=self.cd, amount=20,
+                      start_date=date(2016, 1, 1),
+                      end_date=date(2016, 1, 1))
+
+        OfferSubscription.objects.create(
+            subscription_date=date(2016, 2, 2),
+            member=self.ef,
+            offer=Offer.objects.create(
+                name='fu',
+                period_fees=0,
+                initial_fees=0
+            ),
+        )
+
+    @freeze_time('2016-10-01')
+    def test_could_be_deleted(self):
+        deletion_set = set(Member.objects.could_be_deleted())
+
+        # late on fee (-> delete)
+        self.assertIn(self.cd, deletion_set)
+
+        # no fee at all (-> delete)
+        self.assertIn(self.gh, deletion_set)
+
+        # running fee (-> no delete)
+        self.assertNotIn(self.ab, deletion_set)
+
+        # running service (even if no fee) (-> no delete)
+        self.assertNotIn(self.ef, deletion_set)
+
+
 class MemberTestsUtils(object):
 
     @staticmethod

+ 3 - 0
coin/utils.py

@@ -118,6 +118,9 @@ delete_selected.short_description = "Supprimer tous les objets sélectionnés."
 def in_one_year():
     return date.today() + timedelta(365)
 
+def one_year_ago():
+    return date.today() - timedelta(365)
+
 
 def start_of_month():
     return date(date.today().year, date.today().month, 1)