From 83e49092990ab9d24dd06fe597528f666976fc99 Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Mon, 8 Dec 2025 16:22:19 +0330 Subject: [PATCH] fix - repeatitive closed quotas --- .../commands/rebuild_org_quota_stats.py | 111 ++++++++++++++++++ .../services/quota_dashboard_service.py | 1 + apps/product/web/api/v1/viewsets/quota_api.py | 2 +- 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 apps/product/management/commands/rebuild_org_quota_stats.py diff --git a/apps/product/management/commands/rebuild_org_quota_stats.py b/apps/product/management/commands/rebuild_org_quota_stats.py new file mode 100644 index 0000000..bc7bb0d --- /dev/null +++ b/apps/product/management/commands/rebuild_org_quota_stats.py @@ -0,0 +1,111 @@ +from django.core.management.base import BaseCommand +from django.db import transaction + +from apps.product.models import Quota, OrganizationQuotaStats, QuotaDistribution + + +class Command(BaseCommand): + help = "Ensure each Quota has exactly one OrganizationQuotaStats per related organization" + + @transaction.atomic + def handle(self, *args, **options): + quotas = Quota.objects.prefetch_related('assigned_organizations').select_related( + 'registerer_organization', 'product' + ) + + created = 0 + existing = 0 + updated = 0 + merged = 0 + + for quota in quotas: + + # 1) registerer org + target_orgs = {quota.registerer_organization} + + # 2) assigned orgs + # for org in quota.assigned_organizations.all(): + # target_orgs.add(org) + + for org in target_orgs: + if not org: + continue + + qs = OrganizationQuotaStats.objects.filter( + quota=quota, + organization=org, + ) + + if qs.exists(): + # If multiple exist → keep one, delete others + if qs.count() > 1: + first = qs.first() + qs.exclude(id=first.id).delete() + + existing += 1 + continue + + # Create new one + quota_stat = OrganizationQuotaStats.objects.create( + quota=quota, + organization=org, + total_amount=quota.quota_weight, + remaining_amount=quota.remaining_weight, + total_distributed=quota.quota_distributed, + stat_type="quota" + ) + created += 1 + + # ---- 2) Create OrganizationQuotaStats for each QuotaDistribution ---- + distributions = QuotaDistribution.objects.select_related( + "assigned_organization", "quota" + ) + + for dist in distributions: + org = dist.assigned_organization + quota = dist.quota + + if not quota or not org: + continue + + qs = OrganizationQuotaStats.objects.filter( + quota=quota, organization=org + ) + + if qs.exists(): + if qs.count() > 1: + first = qs.first() + qs.exclude(id=first.id).delete() + merged += 1 + + # Update existing record with distribution weight (optional) + record = qs.first() + record.total_distributed += dist.weight + record.total_amount += dist.weight + record.remaining_amount = max(record.total_amount - record.sold_amount, 0) + record.save() + updated += 1 + + else: + # Create new stats for this distribution + dist_stat = OrganizationQuotaStats.objects.create( + quota=quota, + organization=org, + total_amount=dist.weight, + total_distributed=dist.distributed, + remaining_amount=dist.remaining_weight, + free_sale_balance=dist.free_sale_balance, + pre_sale_balance=dist.pre_sale_balance, + stat_type="distribution", + ) + quota_stat.distributions.add(dist) + created += 1 + + self.stdout.write( + self.style.SUCCESS( + f"OrganizationQuotaStats Rebuild Completed:" + f"\n Created: {created}" + f"\n Updated: {updated}" + f"\n Merged: {merged}" + ) + ) diff --git a/apps/product/services/quota_dashboard_service.py b/apps/product/services/quota_dashboard_service.py index d70b59a..19fe1f9 100644 --- a/apps/product/services/quota_dashboard_service.py +++ b/apps/product/services/quota_dashboard_service.py @@ -18,6 +18,7 @@ class QuotaDashboardService: if org.type.key == 'ADM': org_quota_stats = OrganizationQuotaStats.objects.filter(stat_type='quota', quota__is_closed=quota_is_closed) + print(len(org_quota_stats), quota_is_closed) else: org_quota_stats = OrganizationQuotaStats.objects.filter( organization=org, diff --git a/apps/product/web/api/v1/viewsets/quota_api.py b/apps/product/web/api/v1/viewsets/quota_api.py index 400b9c1..6fa2baf 100644 --- a/apps/product/web/api/v1/viewsets/quota_api.py +++ b/apps/product/web/api/v1/viewsets/quota_api.py @@ -535,7 +535,7 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, QuotaDashboardService, viewsets # paginate queryset page = self.paginate_queryset( - queryset.order_by('-modify_date') + queryset.order_by('-modify_date').distinct() ) if page is not None: # noqa serializer = self.get_serializer(page, many=True)