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.contrib.postgres.fields import ArrayField
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Sum
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from apps.authentication.models import OrganizationType, Organization
|
from apps.authentication.models import OrganizationType, Organization
|
||||||
@@ -468,7 +469,12 @@ class Quota(BaseModel):
|
|||||||
quota=self,
|
quota=self,
|
||||||
organization=org
|
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):
|
def soft_delete(self):
|
||||||
self.trash = True
|
self.trash = True
|
||||||
@@ -808,6 +814,29 @@ class OrganizationQuotaStats(BaseModel):
|
|||||||
|
|
||||||
distributions = models.ManyToManyField(QuotaDistribution)
|
distributions = models.ManyToManyField(QuotaDistribution)
|
||||||
total_amount = models.PositiveBigIntegerField(default=0)
|
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):
|
def __str__(self):
|
||||||
return f"organization: {self.organization} - quota: {self.quota}"
|
return f"organization: {self.organization} - quota: {self.quota}"
|
||||||
|
|||||||
@@ -243,36 +243,17 @@ 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
|
set total received distributions for every organization
|
||||||
"""
|
"""
|
||||||
if distribution:
|
|
||||||
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
|
||||||
quota=quota,
|
|
||||||
organization=distribution.assigned_organization,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# prevent from maximum recursion loop
|
|
||||||
distribution.stat_from_signal = True
|
|
||||||
else:
|
|
||||||
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
org_quota_stat, created = OrganizationQuotaStats.objects.get_or_create(
|
||||||
quota=quota,
|
quota=quota,
|
||||||
organization=quota.registerer_organization,
|
organization=quota.registerer_organization,
|
||||||
total_amount=quota.quota_weight
|
|
||||||
)
|
)
|
||||||
|
org_quota_stat.total_amount = quota.quota_weight
|
||||||
|
org_quota_stat.save(update_fields=['total_amount'])
|
||||||
|
|
||||||
# delete quota
|
# delete quota
|
||||||
if quota.trash:
|
if quota.trash:
|
||||||
@@ -293,8 +274,6 @@ def update_stats_on_change(sender, instance, **kwargs):
|
|||||||
if getattr(instance, 'stat_from_signal', False):
|
if getattr(instance, 'stat_from_signal', False):
|
||||||
return
|
return
|
||||||
|
|
||||||
organization_quota_stats(instance.quota, instance)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver([post_save, post_delete], sender=Quota)
|
@receiver([post_save, post_delete], sender=Quota)
|
||||||
def update_quota_stats_on_change(sender, instance, **kwargs):
|
def update_quota_stats_on_change(sender, instance, **kwargs):
|
||||||
@@ -304,3 +283,45 @@ def update_quota_stats_on_change(sender, instance, **kwargs):
|
|||||||
return
|
return
|
||||||
|
|
||||||
organization_quota_stats(instance)
|
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
|
# change quota weight by organization received weight
|
||||||
if 'org' in self.context.keys():
|
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:
|
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 isinstance(instance, product_models.Quota):
|
||||||
if instance.sale_unit:
|
if instance.sale_unit:
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from apps.product.models import QuotaDistribution
|
|
||||||
|
from apps.product.models import QuotaDistribution, OrganizationQuotaStats
|
||||||
from .models import InventoryEntry, InventoryQuotaSaleItem
|
from .models import InventoryEntry, InventoryQuotaSaleItem
|
||||||
|
|
||||||
|
|
||||||
@@ -67,3 +68,17 @@ def update_distribution_warehouse_sold_and_balance(sender, instance: InventoryQu
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print("quota distribution is null - warehouse app signals")
|
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