add - new changes on OrganizationQuotaStat & calculative signals on quota/distribution
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0 on 2025-11-16 07:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0093_organizationquotastats'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='organizationquotastats',
|
||||
name='remaining_amount',
|
||||
field=models.PositiveBigIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='organizationquotastats',
|
||||
name='sold_amount',
|
||||
field=models.PositiveBigIntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='organizationquotastats',
|
||||
name='total_distributed',
|
||||
field=models.PositiveBigIntegerField(default=0),
|
||||
),
|
||||
]
|
||||
@@ -4,6 +4,7 @@ import jdatetime
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
from apps.authentication.models import OrganizationType, Organization
|
||||
@@ -468,7 +469,12 @@ class Quota(BaseModel):
|
||||
quota=self,
|
||||
organization=org
|
||||
)
|
||||
return stat.first().total_amount if stat.exists() else None
|
||||
return {
|
||||
"quota_weight": stat.first().total_amount if stat.exists() else 0,
|
||||
"remaining_weight": stat.first().remaining_amount if stat.exists() else 0,
|
||||
"quota_distributed": stat.first().total_distributed if stat.exists() else 0,
|
||||
"been_sold": stat.first().sold_amount if stat.exists() else 0,
|
||||
}
|
||||
|
||||
def soft_delete(self):
|
||||
self.trash = True
|
||||
@@ -808,6 +814,29 @@ class OrganizationQuotaStats(BaseModel):
|
||||
|
||||
distributions = models.ManyToManyField(QuotaDistribution)
|
||||
total_amount = models.PositiveBigIntegerField(default=0)
|
||||
total_distributed = models.PositiveBigIntegerField(default=0)
|
||||
sold_amount = models.PositiveBigIntegerField(default=0)
|
||||
remaining_amount = models.PositiveBigIntegerField(default=0) # total - sold
|
||||
|
||||
def update_amount(self):
|
||||
""" calculate total/sold/remaining """
|
||||
from apps.warehouse.models import InventoryQuotaSaleItem
|
||||
|
||||
# calculate total amount of distribution
|
||||
self.total_distributed = self.distributions.filter().aggregate(
|
||||
total=Sum('weight')
|
||||
)['total'] or 0
|
||||
|
||||
self.sold_amount = InventoryQuotaSaleItem.objects.filter(
|
||||
quota_distribution__in=self.distributions.all(),
|
||||
transaction__transaction_status='success'
|
||||
).aggregate(
|
||||
total=Sum('weight')
|
||||
)['total'] or 0
|
||||
|
||||
self.remaining_amount = self.total_amount - self.sold_amount
|
||||
|
||||
self.save()
|
||||
|
||||
def __str__(self):
|
||||
return f"organization: {self.organization} - quota: {self.quota}"
|
||||
|
||||
@@ -243,43 +243,24 @@ def update_quota_stats(instance: Quota):
|
||||
])
|
||||
|
||||
|
||||
def organization_quota_stats(quota: Quota, distribution: QuotaDistribution = None):
|
||||
@receiver(post_save, sender=Quota)
|
||||
def organization_quota_stats(sender, quota: Quota, created: bool, **kwargs):
|
||||
"""
|
||||
set total received distributions for every organization
|
||||
"""
|
||||
if distribution:
|
||||
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
||||
quota=quota,
|
||||
organization=distribution.assigned_organization,
|
||||
)
|
||||
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
||||
quota=quota,
|
||||
organization=quota.registerer_organization,
|
||||
)
|
||||
org_quota_stat.total_amount = quota.quota_weight
|
||||
org_quota_stat.save(update_fields=['total_amount'])
|
||||
|
||||
# delete distribution
|
||||
# decrease org stat total amount after remove distribution
|
||||
if distribution.trash:
|
||||
org_quota_stat.total_amount -= distribution.weight
|
||||
org_quota_stat.save(update_fields=["total_amount"])
|
||||
else:
|
||||
# if stat was created before or total amount is 0
|
||||
if not created or org_quota_stat.total_amount == 0:
|
||||
org_quota_stat.total_amount += distribution.weight
|
||||
org_quota_stat.save(update_fields=["total_amount"])
|
||||
org_quota_stat.distributions.add(distribution)
|
||||
# delete quota
|
||||
if quota.trash:
|
||||
org_quota_stat.soft_delete()
|
||||
|
||||
# prevent from maximum recursion loop
|
||||
distribution.stat_from_signal = True
|
||||
else:
|
||||
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
||||
quota=quota,
|
||||
organization=quota.registerer_organization,
|
||||
total_amount=quota.quota_weight
|
||||
)
|
||||
|
||||
# delete quota
|
||||
if quota.trash:
|
||||
org_quota_stat.soft_delete()
|
||||
|
||||
# prevent from maximum recursion loop
|
||||
quota.stat_from_signal = True
|
||||
# prevent from maximum recursion loop
|
||||
quota.stat_from_signal = True
|
||||
|
||||
|
||||
@receiver([post_save, post_delete], sender=QuotaDistribution)
|
||||
@@ -293,8 +274,6 @@ def update_stats_on_change(sender, instance, **kwargs):
|
||||
if getattr(instance, 'stat_from_signal', False):
|
||||
return
|
||||
|
||||
organization_quota_stats(instance.quota, instance)
|
||||
|
||||
|
||||
@receiver([post_save, post_delete], sender=Quota)
|
||||
def update_quota_stats_on_change(sender, instance, **kwargs):
|
||||
@@ -304,3 +283,45 @@ def update_quota_stats_on_change(sender, instance, **kwargs):
|
||||
return
|
||||
|
||||
organization_quota_stats(instance)
|
||||
|
||||
|
||||
@receiver(post_save, sender=QuotaDistribution)
|
||||
def update_quota_stats_on_distribution(sender, instance: QuotaDistribution, created, **kwargs):
|
||||
if getattr(instance, 'one_time_loop_flag', False):
|
||||
return
|
||||
|
||||
if instance.trash:
|
||||
return
|
||||
|
||||
org = instance.assigned_organization
|
||||
quota = instance.quota
|
||||
|
||||
stats, _ = OrganizationQuotaStats.objects.get_or_create(
|
||||
quota=quota,
|
||||
organization=org,
|
||||
)
|
||||
stats.distributions.add(instance)
|
||||
stats.update_amount()
|
||||
instance.one_time_loop_flag = True
|
||||
|
||||
|
||||
@receiver(post_save, sender=QuotaDistribution)
|
||||
def handle_quota_stats_soft_delete_on_distribution(sender, instance: QuotaDistribution, created, **kwargs):
|
||||
if getattr(instance, 'one_time_loop_flag', False):
|
||||
return
|
||||
|
||||
if instance.trash:
|
||||
org = instance.assigned_organization
|
||||
quota = instance.quota
|
||||
|
||||
stats_qs = OrganizationQuotaStats.objects.filter(
|
||||
quota=quota,
|
||||
organization=org,
|
||||
)
|
||||
|
||||
if stats_qs:
|
||||
for stats in stats_qs:
|
||||
stats.distributions.remove(instance)
|
||||
stats.update_amount()
|
||||
|
||||
instance.one_time_loop_flag = True
|
||||
|
||||
@@ -15,9 +15,19 @@ class QuotaSerializer(serializers.ModelSerializer):
|
||||
|
||||
# change quota weight by organization received weight
|
||||
if 'org' in self.context.keys():
|
||||
quota_weight_by_org = instance.quota_amount_by_org(self.context['org'])
|
||||
org = self.context['org']
|
||||
quota_weight_by_org = instance.quota_amount_by_org(org)
|
||||
if quota_weight_by_org:
|
||||
representation['quota_weight'] = quota_weight_by_org
|
||||
representation['quota_weight'] = quota_weight_by_org['quota_weight']
|
||||
representation['quota_distributed'] = quota_weight_by_org['quota_distributed']
|
||||
representation['remaining_weight'] = quota_weight_by_org['remaining_weight']
|
||||
representation['been_sold'] = quota_weight_by_org['been_sold']
|
||||
representation['distributions'] = [{
|
||||
"id": dist.id,
|
||||
"organization": org.name,
|
||||
"organization_id": org.id,
|
||||
"weight": dist.weight,
|
||||
} for dist in instance.distributions_assigned.all()]
|
||||
|
||||
if isinstance(instance, product_models.Quota):
|
||||
if instance.sale_unit:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from django.db.models import Sum
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
from apps.product.models import QuotaDistribution
|
||||
|
||||
from apps.product.models import QuotaDistribution, OrganizationQuotaStats
|
||||
from .models import InventoryEntry, InventoryQuotaSaleItem
|
||||
|
||||
|
||||
@@ -67,3 +68,17 @@ def update_distribution_warehouse_sold_and_balance(sender, instance: InventoryQu
|
||||
)
|
||||
else:
|
||||
print("quota distribution is null - warehouse app signals")
|
||||
|
||||
|
||||
@receiver(post_save, sender=InventoryQuotaSaleItem)
|
||||
def update_quota_stats_on_sale(sender, instance: InventoryQuotaSaleItem, created, **kwargs):
|
||||
if instance.transaction.transaction_status == 'success':
|
||||
return
|
||||
|
||||
stats = OrganizationQuotaStats.objects.filter(
|
||||
organization=instance.quota_distribution.assigned_organization,
|
||||
quota=instance.quota_distribution.quota
|
||||
).first()
|
||||
|
||||
if stats:
|
||||
stats.update_amounts()
|
||||
|
||||
Reference in New Issue
Block a user