diff --git a/apps/product/management/commands/rebuild_org_quota_stats.py b/apps/product/management/commands/rebuild_org_quota_stats.py index bc7bb0d..03487cf 100644 --- a/apps/product/management/commands/rebuild_org_quota_stats.py +++ b/apps/product/management/commands/rebuild_org_quota_stats.py @@ -12,7 +12,7 @@ class Command(BaseCommand): quotas = Quota.objects.prefetch_related('assigned_organizations').select_related( 'registerer_organization', 'product' ) - + print("sssss") created = 0 existing = 0 updated = 0 @@ -43,23 +43,24 @@ class Command(BaseCommand): qs.exclude(id=first.id).delete() existing += 1 - continue + # 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 + # 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" ) + print(len(distributions)) for dist in distributions: org = dist.assigned_organization @@ -68,27 +69,27 @@ class Command(BaseCommand): if not quota or not org: continue - qs = OrganizationQuotaStats.objects.filter( + dist_qs = OrganizationQuotaStats.objects.filter( quota=quota, organization=org ) - if qs.exists(): - if qs.count() > 1: - first = qs.first() - qs.exclude(id=first.id).delete() + if dist_qs.exists(): + if dist_qs.count() > 1: + first = dist_qs.first() + dist_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 + # 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( + dist_qs = OrganizationQuotaStats.objects.create( quota=quota, organization=org, total_amount=dist.weight, @@ -98,7 +99,7 @@ class Command(BaseCommand): pre_sale_balance=dist.pre_sale_balance, stat_type="distribution", ) - quota_stat.distributions.add(dist) + qs.first().distributions.add(dist) created += 1 self.stdout.write( diff --git a/apps/product/services/quota_dashboard_service.py b/apps/product/services/quota_dashboard_service.py index 97efa58..50df7b4 100644 --- a/apps/product/services/quota_dashboard_service.py +++ b/apps/product/services/quota_dashboard_service.py @@ -16,14 +16,27 @@ class QuotaDashboardService: def get_dashboard(self, org: Organization, start_date: str = None, end_date: str = None, search_fields: list[str] = None, quota_is_closed: bool = False, query_string: str = None): + distribution_number = 0 + 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) + org_quota_stats = OrganizationQuotaStats.objects.filter( + stat_type='quota', + quota__is_closed=quota_is_closed + ) + dist_org_quota_stats = OrganizationQuotaStats.objects.filter( + stat_type='distribution', + quota__is_closed=quota_is_closed + ) else: org_quota_stats = OrganizationQuotaStats.objects.filter( organization=org, quota__is_closed=quota_is_closed, ) + dist_org_quota_stats = OrganizationQuotaStats.objects.filter( + stat_type='distribution', + organization=org, + quota__is_closed=quota_is_closed + ) # filter queryset (transactions & items) by date if (start_date and end_date) or query_string: @@ -36,6 +49,19 @@ class QuotaDashboardService: query_string=query_string ).apply() + for stat in org_quota_stats.distinct('quota'): + if org.type.key == 'ADM': + distribution_number += QuotaDistribution.objects.select_related('quota').filter( + quota=stat.quota, + quota__is_closed=False + ).count() + else: + distribution_number += QuotaDistribution.objects.filter( + Q(assigner_organization=org), + quota=stat.quota, + quota__is_closed=False + ).count() + org_quota_stats = org_quota_stats.aggregate( total_quotas=Count("quota", distinct=True), total_distributed=Coalesce(Sum("total_distributed", ), 0), @@ -46,12 +72,25 @@ class QuotaDashboardService: inventory_entry_balance=Coalesce(Sum("inventory_entry_balance", ), 0), ) + org_quota_stats.update( + distribution_number=distribution_number, + dist_remaining_amount=dist_org_quota_stats.aggregate( + remaining_amount=Coalesce(Sum("remaining_amount"), 0), )['remaining_amount'] or 0, + ) + return { "quotas_summary": org_quota_stats, } @staticmethod - def get_dashboard_by_product(self, organization: Organization, products: dict): + def get_dashboard_by_product( + self, + organization: Organization, + products: dict, + start_date: str = None, end_date: str = None, + search_fields: list[str] = None, quota_is_closed: bool = False, + query_string: str = None + ): stat_by_prod = [] for prod_name, prod_id in products.items(): @@ -59,6 +98,18 @@ class QuotaDashboardService: organization=organization, quota__product_id=prod_id ) + + # filter queryset (transactions & items) by date + if (start_date and end_date) or query_string: + org_quota_stats = DynamicSearchService( + queryset=org_quota_stat, + start=start_date, + end=end_date, + date_field="create_date", + search_fields=search_fields, + query_string=query_string + ).apply() + product_stat_data = org_quota_stat.aggregate( quotas_count=Count('id'), total_quotas_weight=Coalesce(models.Sum('total_amount'), 0), @@ -88,13 +139,13 @@ class QuotaDashboardService: # product total distributed weight from quota given_distribution_weight = QuotaDistribution.objects.select_related( - 'quota', 'assigned_organization' + 'quota', 'assigner_organization' ).filter( quota__product_id=prod_id, quota__is_closed=False, assigner_organization=organization, ) if not organization.type.key == 'ADM' else QuotaDistribution.objects.select_related( - 'quota', 'assigned_organization' + 'quota', 'assigner_organization' ).filter( quota__product_id=prod_id, quota__is_closed=False, diff --git a/apps/product/web/api/v1/viewsets/quota_api.py b/apps/product/web/api/v1/viewsets/quota_api.py index 286a9bc..5ea4592 100644 --- a/apps/product/web/api/v1/viewsets/quota_api.py +++ b/apps/product/web/api/v1/viewsets/quota_api.py @@ -410,12 +410,23 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, QuotaDashboardService, viewsets dashboard of all quotas & their information """ + search_fields = [ + "quota__registerer_organization__name", + "quota__quota_id", + "quota__product__name", + "quota__sale_type", + "quota__sale_unit__unit", + "quota__group", + "quota__creator_info", + ] + org = get_organization_by_user(request.user) query_param = self.request.query_params # noqa # filter by date start_date = query_param.get('start') if 'start' in query_param.keys() else None end_date = query_param.get('end') if 'end' in query_param.keys() else None + query_string = query_param.get('search') if 'search' in query_param.keys() else None # filter by quota is close or open is_closed = True if 'is_closed' in query_param.keys() else False @@ -428,7 +439,16 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, QuotaDashboardService, viewsets products = {f'{stat.quota.product.name}': stat.quota.product.id for stat in org_quota_stat} - dashboard_data_by_product = self.get_dashboard_by_product(self, org, products) + dashboard_data_by_product = self.get_dashboard_by_product( + self, + org, + products, + start_date=start_date, + end_date=end_date, + search_fields=search_fields, + quota_is_closed=is_closed, + query_string=query_string + ) return Response(dashboard_data_by_product) diff --git a/apps/warehouse/web/api/v1/serializers.py b/apps/warehouse/web/api/v1/serializers.py index e3318d1..6ddf252 100644 --- a/apps/warehouse/web/api/v1/serializers.py +++ b/apps/warehouse/web/api/v1/serializers.py @@ -126,7 +126,11 @@ class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer): 'id': instance.rancher.id, 'national_code': instance.rancher.national_code } - + if instance.pos_device: + representation['pos_device'] = { + 'device_identity': instance.pos_device.device_identity, + 'serial': instance.pos_device.serial + } if instance.seller_organization: representation['seller_organization'] = { 'id': instance.seller_organization.id,