Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 57 additions & 8 deletions user_profile/admin_sede_members.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,64 @@
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.db.models import Count, Q
from django.shortcuts import render
from django.shortcuts import redirect, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods

from user_profile.models import Profile
from user_profile.models import Profile, SedeSubscription, SedeSubscriptionPlan
from user_profile.services.sede_mercadopago import format_payment_method


def _attach_plan_billing_cycle(subscriptions, plan_billing_cycles):
for subscription in subscriptions:
subscription.billing_cycle = plan_billing_cycles.get(subscription.plan_id, '')
return subscriptions


@staff_member_required
@require_http_methods(['GET'])
@require_http_methods(['GET', 'POST'])
def admin_sede_members_view(request):
if request.method == 'POST':
action = request.POST.get('action') or ''
subscription_id = request.POST.get('subscription_id') or ''
if action == 'soft_remove' and subscription_id:
updated = SedeSubscription.objects.filter(
subscription_id=subscription_id,
is_soft_removed=False,
).update(
is_soft_removed=True,
is_active=False,
status='removed',
soft_removed_at=timezone.now(),
synced_at=timezone.now(),
)
if updated:
messages.success(request, f'Subscription {subscription_id} was soft-removed from member sync.')
else:
messages.warning(request, f'Subscription {subscription_id} was already removed or does not exist.')
else:
messages.error(request, 'Invalid soft-remove request.')
return redirect('admin_sede_members_view')

profiles = (
Profile.objects.filter(sede_subscriptions__isnull=False)
Profile.objects.filter(sede_subscriptions__isnull=False, sede_subscriptions__is_soft_removed=False)
.select_related('user')
.prefetch_related('sede_subscriptions')
.distinct()
.order_by('user__last_name', 'user__first_name', 'user__email')
)

plan_billing_cycles = {
plan_id: billing_cycle
for plan_id, billing_cycle in SedeSubscriptionPlan.objects.values_list('plan_id', 'billing_cycle')
}

rows = []
for profile in profiles:
subs = list(profile.sede_subscriptions.all().order_by('-is_active', '-last_payment_date', '-synced_at'))
subs = list(
profile.sede_subscriptions.filter(is_soft_removed=False).order_by('-is_active', '-last_payment_date', '-synced_at')
)
_attach_plan_billing_cycle(subs, plan_billing_cycles)
rows.append({
'profile': profile,
'subscriptions': subs,
Expand All @@ -43,19 +81,30 @@ def admin_sede_members_view(request):
@require_http_methods(['GET'])
def admin_sede_multiple_active_view(request):
profiles = (
Profile.objects.filter(sede_subscriptions__isnull=False)
Profile.objects.filter(sede_subscriptions__isnull=False, sede_subscriptions__is_soft_removed=False)
.annotate(
active_count=Count('sede_subscriptions', filter=Q(sede_subscriptions__is_active=True))
active_count=Count(
'sede_subscriptions',
filter=Q(sede_subscriptions__is_active=True, sede_subscriptions__is_soft_removed=False),
)
)
.filter(active_count__gt=1)
.select_related('user')
.prefetch_related('sede_subscriptions')
.order_by('-active_count', 'user__last_name', 'user__first_name', 'user__email')
)

plan_billing_cycles = {
plan_id: billing_cycle
for plan_id, billing_cycle in SedeSubscriptionPlan.objects.values_list('plan_id', 'billing_cycle')
}

rows = []
for profile in profiles:
subs = list(profile.sede_subscriptions.all().order_by('-is_active', '-last_payment_date', '-synced_at'))
subs = list(
profile.sede_subscriptions.filter(is_soft_removed=False).order_by('-is_active', '-last_payment_date', '-synced_at')
)
_attach_plan_billing_cycle(subs, plan_billing_cycles)
rows.append({
'profile': profile,
'subscriptions': subs,
Expand Down
10 changes: 8 additions & 2 deletions user_profile/management/commands/sync_sede_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ def handle(self, *args, **options):

self.stdout.write(
self.style.SUCCESS(
f"Sync complete: {summary['matched']} matched, "
f"Sync complete: {summary['total']} total, "
f"{summary.get('authorized_total', 0)} authorized, "
f"{summary.get('update_only_total', 0)} non-auth, "
f"{summary['matched']} matched, "
f"{summary['unmatched']} unmatched, "
f"{summary.get('update_only_ignored', 0)} non-auth ignored, "
f"{summary.get('soft_removed_skipped', 0)} soft-removed skipped, "
f"{summary['conflicts']} conflicts, "
f"{summary['errors']} errors, "
f"{summary['active_members']} active members, "
f"{summary['deactivated']} deactivated"
f"{summary['deactivated']} deactivated, "
f"took {summary.get('duration_seconds', 0)}s"
)
)
21 changes: 21 additions & 0 deletions user_profile/migrations/0010_sedesubscription_soft_remove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('user_profile', '0009_sedesubscriptionplan_billing_cycle'),
]

operations = [
migrations.AddField(
model_name='sedesubscription',
name='is_soft_removed',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='sedesubscription',
name='soft_removed_at',
field=models.DateTimeField(blank=True, null=True),
),
]
6 changes: 4 additions & 2 deletions user_profile/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Profile(BaseModel):
def _primary_sede_subscription(self):
if hasattr(self, '_cached_primary_sede_subscription'):
return self._cached_primary_sede_subscription
subs_qs = self.sede_subscriptions.all()
subs_qs = self.sede_subscriptions.filter(is_soft_removed=False)
active = subs_qs.filter(is_active=True).order_by('-last_payment_date', '-synced_at').first()
if active:
self._cached_primary_sede_subscription = active
Expand All @@ -47,7 +47,7 @@ def _primary_sede_subscription(self):

@property
def miembro_sede(self):
return self.sede_subscriptions.filter(is_active=True).exists()
return self.sede_subscriptions.filter(is_active=True, is_soft_removed=False).exists()

@property
def sede_subscription_id(self):
Expand Down Expand Up @@ -145,6 +145,8 @@ class SedeSubscription(BaseModel):
next_payment_date = models.DateTimeField(null=True, blank=True)
member_since = models.DateTimeField(null=True, blank=True)
is_active = models.BooleanField(default=False)
is_soft_removed = models.BooleanField(default=False)
soft_removed_at = models.DateTimeField(null=True, blank=True)
matched_via = models.CharField(max_length=16, blank=True, default='')
synced_at = models.DateTimeField(null=True, blank=True)

Expand Down
Loading
Loading