From 67904fd1a880f9c000e1d41bb9c444697c8dd63c Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Tue, 25 Nov 2025 10:20:51 +0330 Subject: [PATCH] fix - caculate my childs quota stat / add assigned orgs & assigned_to_me status --- apps/product/models.py | 31 +++++ .../api/v1/serializers/quota_serializers.py | 126 ++++++++++-------- apps/product/web/api/v1/viewsets/quota_api.py | 2 - 3 files changed, 98 insertions(+), 61 deletions(-) diff --git a/apps/product/models.py b/apps/product/models.py index a308264..9963528 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -8,6 +8,7 @@ from django.db.models import Sum from simple_history.models import HistoricalRecords from apps.authentication.models import OrganizationType, Organization +from apps.authentication.services.service import get_all_org_child from apps.authorization.models import UserRelations from apps.core.models import BaseModel from apps.herd.models import Rancher @@ -465,10 +466,40 @@ class Quota(BaseModel): return persian_date.month in self.sale_license def quota_amount_by_org(self, org: Organization): + """ + get stats of quota from org quota stat model + """ stat = OrganizationQuotaStats.objects.filter( quota=self, organization=org ) + + if not stat.exists(): + # get childs of organization + org_childs = get_all_org_child(org) # noqa + + # calculate total stats of child orgs + stat = OrganizationQuotaStats.objects.filter( + quota=self, + organization_id__in=[child.id for child in org_childs] + ).aggregate( + total_quota_weight=models.Sum('total_amount'), + total_remaining_weight=models.Sum('remaining_amount'), + total_quota_distributed=models.Sum('total_distributed'), + total_been_sold=models.Sum('sold_amount'), + total_inventory_received=models.Sum('inventory_received'), + total_inventory_entry_balance=models.Sum('inventory_entry_balance') + ) + return { + "id": 0, + "quota_weight": stat['total_quota_weight'], + "remaining_weight": stat['total_remaining_weight'], + "quota_distributed": stat['total_quota_distributed'], + "been_sold": stat['total_been_sold'], + "inventory_received": stat['total_inventory_received'], + "inventory_entry_balance": stat['total_inventory_entry_balance'], + } + return { "id": stat.first().id if stat.exists() else 0, "quota_weight": stat.first().total_amount if stat.exists() else 0, diff --git a/apps/product/web/api/v1/serializers/quota_serializers.py b/apps/product/web/api/v1/serializers/quota_serializers.py index 86bbb59..4cd5e35 100644 --- a/apps/product/web/api/v1/serializers/quota_serializers.py +++ b/apps/product/web/api/v1/serializers/quota_serializers.py @@ -24,6 +24,8 @@ class QuotaSerializer(serializers.ModelSerializer): def to_representation(self, instance: product_models.Quota): representation = super().to_representation(instance) + assigned_orgs = instance.assigned_organizations.all() + # change quota weight by organization received weight if 'org' in self.context.keys(): org = self.context['org'] @@ -51,71 +53,77 @@ class QuotaSerializer(serializers.ModelSerializer): "weight": dist.weight, } for dist in instance.distributions_assigned.filter(assigned_organization=org)] - if isinstance(instance, product_models.Quota): - if instance.sale_unit: - representation['sale_unit'] = product_serializers.SaleUnitSerializer( - instance.sale_unit - ).data - representation['product'] = {"product": instance.product.name, "product_id": instance.product.id} + representation['assigned_to_me'] = True if org in assigned_orgs else False - # quota incentive plan data - incentive_plan_map = {} - for assign in instance.incentive_assignments.all(): - plan_id = assign.incentive_plan.id - if plan_id not in incentive_plan_map: - incentive_plan_map[plan_id] = { - "id": assign.id, - "name": assign.incentive_plan.name, - "incentive_plan": plan_id, - "live_stocks": [] - } + # list of assigned organizations that received this quota + representation['assigned_organizations'] = [{ + "name": org.name, "id": org.id + } for org in assigned_orgs] - if assign.livestock_type: - incentive_plan_map[plan_id]['live_stocks'].append({ - "id": assign.livestock_type.id, - "name": assign.livestock_type.name, - "quantity": assign.quantity_kg - }) - - representation['incentive_plan'] = list(incentive_plan_map.values()) - - representation['attribute_values'] = product_serializers.AttributeValueSerializer( - instance.attribute_values.all(), - many=True + if instance.sale_unit: + representation['sale_unit'] = product_serializers.SaleUnitSerializer( + instance.sale_unit ).data + representation['product'] = {"product": instance.product.name, "product_id": instance.product.id} - representation['brokers'] = QuotaBrokerValueSerializer( - instance.broker_values.all(), - many=True - ).data - - representation['livestock_allocations'] = QuotaLiveStockAllocationSerializer( - instance.livestock_allocations.all(), - many=True - ).data - - representation['livestock_limitations'] = QuotaLiveStockAgeLimitationSerializer( - instance.livestock_age_limitations.all(), - many=True - ).data - - representation['limit_by_organizations'] = [ - {"name": limit.name, "id": limit.id} for limit in instance.limit_by_organizations.all() - ] - - # Build a simplified list of pricing items for API output: - # map `pricing_type_id` to `pricing_type` and keep `name` and `value` - items = [ - { - "pricing_type": it["pricing_type_id"], - "pricing_type_name": it["pricing_type__name"], - "name": it["name"], - "value": it["value"], + # quota incentive plan data + incentive_plan_map = {} + for assign in instance.incentive_assignments.all(): + plan_id = assign.incentive_plan.id + if plan_id not in incentive_plan_map: + incentive_plan_map[plan_id] = { + "id": assign.id, + "name": assign.incentive_plan.name, + "incentive_plan": plan_id, + "live_stocks": [] } - for it in instance.pricing_items.values("pricing_type_id", "pricing_type__name", "name", "value") - ] - representation["price_calculation_items"] = items + if assign.livestock_type: + incentive_plan_map[plan_id]['live_stocks'].append({ + "id": assign.livestock_type.id, + "name": assign.livestock_type.name, + "quantity": assign.quantity_kg + }) + + representation['incentive_plan'] = list(incentive_plan_map.values()) + + representation['attribute_values'] = product_serializers.AttributeValueSerializer( + instance.attribute_values.all(), + many=True + ).data + + representation['brokers'] = QuotaBrokerValueSerializer( + instance.broker_values.all(), + many=True + ).data + + representation['livestock_allocations'] = QuotaLiveStockAllocationSerializer( + instance.livestock_allocations.all(), + many=True + ).data + + representation['livestock_limitations'] = QuotaLiveStockAgeLimitationSerializer( + instance.livestock_age_limitations.all(), + many=True + ).data + + representation['limit_by_organizations'] = [ + {"name": limit.name, "id": limit.id} for limit in instance.limit_by_organizations.all() + ] + + # Build a simplified list of pricing items for API output: + # map `pricing_type_id` to `pricing_type` and keep `name` and `value` + items = [ + { + "pricing_type": it["pricing_type_id"], + "pricing_type_name": it["pricing_type__name"], + "name": it["name"], + "value": it["value"], + } + for it in instance.pricing_items.values("pricing_type_id", "pricing_type__name", "name", "value") + ] + + representation["price_calculation_items"] = items return representation diff --git a/apps/product/web/api/v1/viewsets/quota_api.py b/apps/product/web/api/v1/viewsets/quota_api.py index f717d60..22981f3 100644 --- a/apps/product/web/api/v1/viewsets/quota_api.py +++ b/apps/product/web/api/v1/viewsets/quota_api.py @@ -359,8 +359,6 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, viewsets.ModelViewSet, DynamicS self.get_queryset(visibility_by_org_scope=True).filter( is_closed=False)) # return by search param or all objects - print(queryset) - # paginate queryset page = self.paginate_queryset( queryset.filter(