import - distributioin stat type for distribution tab

This commit is contained in:
2025-12-09 11:28:06 +03:30
parent f4958e657e
commit 9aef4967c7
4 changed files with 107 additions and 31 deletions

View File

@@ -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(

View File

@@ -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,

View File

@@ -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)

View File

@@ -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,