optimize load balance for inventory list in pos - add new fields to stake holdes

This commit is contained in:
2025-08-31 14:51:08 +03:30
parent dd5be869c5
commit 27046f20e9
8 changed files with 118 additions and 113 deletions

View File

@@ -3,36 +3,27 @@ from apps.herd.models import Rancher
from apps.livestock.models import LiveStock
from apps.warehouse.models import InventoryEntry
from apps.product.models import Quota
from django.db.models import Count, Q
import typing
def get_rancher_statistics(rancher: Rancher = None) -> typing.Any:
""" get statistics of a rancher """ # noqa
herds = rancher.herd.all() # noqa
herd_count = herds.count()
livestocks = LiveStock.objects.filter(herd__rancher=rancher) # noqa
livestocks = LiveStock.objects.filter(herd__in=herds) # noqa
stats = livestocks.aggregate(
herd_count=Count("herd", distinct=True),
light_count=Count('id', filter=Q(weight_type='L')),
heavy_count=Count('id', filter=Q(weight_type='H')),
sheep_count=Count('id', filter=Q(type__name='گوسفند')), # noqa
goat_count=Count('id', filter=Q(type__name='بز')),
cow_count=Count('id', filter=Q(type__name='گاو')),
camel_count=Count('id', filter=Q(type__name='شتر')),
horse_count=Count('id', filter=Q(type__name='بز')),
)
light_count = livestocks.filter(weight_type='L').count()
heavy_count = livestocks.filter(weight_type='H').count()
sheep_count = livestocks.filter(type__name="گوسفند").count() # noqa
goat_count = livestocks.filter(type__name="بز").count()
cow_count = livestocks.filter(type__name="گاو").count()
camel_count = livestocks.filter(type__name="شتر").count()
horse_count = livestocks.filter(type__name="اسب").count()
return {
"herd_count": herd_count,
"light_count": light_count,
"heavy_count": heavy_count,
"sheep_count": sheep_count,
"goat_count": goat_count,
"cow_count": cow_count,
"camel_count": camel_count,
"horse_count": horse_count,
}
return stats
def rancher_quota_weight(rancher, inventory_entry: InventoryEntry):
@@ -51,60 +42,38 @@ def rancher_quota_weight(rancher, inventory_entry: InventoryEntry):
}
quota: Quota = inventory_entry.distribution.quota
allocations = quota.livestock_allocations.all() # list of quota live stock allocations
# list of quota live stock allocations
allocations = list(quota.livestock_allocations.all().select_related('livestock_type'))
# list of quota incentive plans
incentive_plans = list(quota.incentive_assignments.all().select_related('livestock_type'))
livestock_counts = get_rancher_statistics(rancher)
total_weight = 0
alloc_details = {}
plan_details = {}
result_list = []
merged = {}
# list of quota allocations, get allocations weight on any animal type
for alloc in allocations: # noqa
if alloc.livestock_type:
animal_type = alloc.livestock_type.name
per_head = alloc.quantity_kg
for item in allocations + incentive_plans: # noqa
if item.livestock_type:
animal_type = item.livestock_type.name
per_head = item.quantity_kg
count = livestock_counts.get(live_stock_meta.get(animal_type), 0)
weight = per_head * count
alloc_details[animal_type] = {"weight": weight, "type": alloc.livestock_type.weight_type}
total_weight += weight
# list of quota incentive plans, get plans weight on any animal type
incentive_plans = quota.incentive_assignments.all()
for plan in incentive_plans: # noqa
if plan.livestock_type:
animal_type = plan.livestock_type.name
per_head = plan.quantity_kg
count = livestock_counts.get(live_stock_meta.get(animal_type), 0)
if animal_type not in merged:
merged[animal_type] = {
"weight": weight,
"type": item.livestock_type.weight_type
}
else:
merged[animal_type]['weight'] += weight
weight = per_head * count
plan_details[animal_type] = {"weight": weight, "type": plan.livestock_type.weight_type}
total_weight += weight
# summation of incentive plans & livestock allocations animal types weight
result_details = {"total": total_weight, 'by_type': {}}
all_keys = set(alloc_details.keys()) | set(plan_details.keys()) # get all keys from plan & allocations data
for key in all_keys:
alloc_weight = alloc_details.get(key, {}).get("weight", 0) # total weight of quota livestock allocations data
plan_weight = plan_details.get(key, {}).get("weight", 0) # total weight of quota incentive plan data
# get animal type (Heavy, Light)
animal_type = alloc_details.get(
key, {}
).get("type") or plan_details.get(
key, {}
).get("type")
# final result, total weights
result_list.append({
return {
"total_weight": total_weight,
"by_type": [{
"name": key,
"weight": alloc_weight + plan_weight,
"type": animal_type
})
result_details['by_type'] = result_list
return result_details
"weight": value['weight'],
"type": value['type']
}for key, value in merged.items()]
}

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.0 on 2025-08-31 11:17
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0064_brokerstakeholderassignment'),
('product', '0071_quotaincentiveassignment_livestock_type_and_more'),
]
operations = [
migrations.AddField(
model_name='stakeholders',
name='broker',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pos_stake_holders', to='product.broker'),
),
migrations.AddField(
model_name='stakeholders',
name='broker_amount',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pos_stake_holders', to='product.quotabrokervalue'),
),
migrations.DeleteModel(
name='BrokerStakeHolderAssignment',
),
]

View File

@@ -1,13 +1,11 @@
import random
import string
from apps.authentication.models import Organization
from apps.product.models import Broker
from apps.product.models import Product
from apps.product.models import Product, Broker, QuotaBrokerValue
from django.contrib.postgres.fields import ArrayField
from apps.authorization.models import UserRelations
from apps.authentication.models import Organization
from apps.core.models import BaseModel
from django.db import models
import random
import string
class ProviderCompany(BaseModel):
@@ -258,6 +256,18 @@ class StakeHolders(BaseModel):
related_name='pos_stake_holders',
null=True
)
broker = models.ForeignKey(
Broker,
on_delete=models.CASCADE,
related_name='pos_stake_holders',
null=True
)
broker_amount = models.ForeignKey(
QuotaBrokerValue,
on_delete=models.CASCADE,
related_name='pos_stake_holders',
null=True
)
share_percent = models.FloatField(default=0)
default = models.BooleanField(default=False)
@@ -295,29 +305,3 @@ class POSFreeProducts(BaseModel):
def save(self, *args, **kwargs):
return super(POSFreeProducts, self).save(*args, **kwargs)
class BrokerStakeHolderAssignment(BaseModel):
device = models.ForeignKey(
Device,
on_delete=models.CASCADE,
related_name="stake_brok_assigment",
null=True
)
stake_holder = models.ForeignKey(
StakeHolders,
on_delete=models.CASCADE,
related_name='stake_brok_assignment',
null=True
)
broker = models.ForeignKey(
Broker,
on_delete=models.CASCADE,
related_name='stake_brok_assignment',
null=True
)
def save(self, *args, **kwargs):
return super(BrokerStakeHolderAssignment, self).save(*args, **kwargs)

View File

@@ -86,9 +86,3 @@ class StakeHoldersSerializer(ModelSerializer):
).data
return representation
class BrokerStakeHolderAssignSerializer(ModelSerializer):
class Meta:
model = pos_models.BrokerStakeHolderAssignment
fields = '__all__'

View File

@@ -10,7 +10,6 @@ router.register(r'provider', device_views.ProviderCompanyViewSet, basename='prov
router.register(r'device', device_views.DeviceViewSet, basename='device')
router.register(r'device_assignment', device_views.DeviceAssignmentViewSet, basename='device_assignment')
router.register(r'stake_holders', device_views.StakeHoldersViewSet, basename='stake_holders')
router.register(r'broker_stake_assign', device_views.BrokerStakeHolderAssignViewSet, basename='broker_stake_assign')
urlpatterns = [
path('v1/pos/', include(router.urls))

View File

@@ -325,7 +325,3 @@ class StakeHoldersViewSet(viewsets.ModelViewSet, DynamicSearchMixin, SoftDeleteM
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
class BrokerStakeHolderAssignViewSet(viewsets.ModelViewSet, DynamicSearchMixin, SoftDeleteMixin):
queryset = pos_models.BrokerStakeHolderAssignment.objects.all()
serializer_class = device_serializer.BrokerStakeHolderAssignSerializer

View File

@@ -20,7 +20,7 @@ def get_products_in_warehouse(organization_id):
def quota_live_stock_allocation_info(quota: Quota) -> typing.Any:
""" information of quota live stock allocations """
allocations = quota.livestock_allocations.filter(quota=quota)
allocations = quota.livestock_allocations.select_related('livestock_type')
if allocations:
allocations_list = [{
@@ -35,7 +35,7 @@ def quota_live_stock_allocation_info(quota: Quota) -> typing.Any:
def quota_incentive_plans_info(quota: Quota) -> typing.Any:
""" information of quota incentive plans """
incentive_plans = quota.incentive_assignments.all()
incentive_plans = quota.incentive_assignments.select_related("livestock_type", "incentive_plan")
if incentive_plans:
incentive_plans_list = [{
@@ -48,3 +48,31 @@ def quota_incentive_plans_info(quota: Quota) -> typing.Any:
} for plan in incentive_plans]
return incentive_plans_list
def quota_brokers_value(quota: Quota) -> typing.Any:
""" information of quota brokers with their quota value """
brokers = quota.broker_values.select_related("broker")
if brokers:
broker_values_list = [{
'name': broker.broker.name,
'amount': broker.value
} for broker in brokers]
return broker_values_list
def quota_attribute_value(quota: Quota) -> typing.Any:
""" information of quota pricing attribute values """
attributes = quota.attribute_values.select_related("attribute")
if attributes:
attribute_values_list = [{
'name': attr.attribute.name,
'amount': attr.value
}for attr in attributes]
return attribute_values_list

View File

@@ -1,7 +1,9 @@
from apps.herd.services.services import get_rancher_statistics, rancher_quota_weight
from apps.product.services.services import (
quota_live_stock_allocation_info,
quota_incentive_plans_info
quota_incentive_plans_info,
quota_brokers_value,
quota_attribute_value
)
from apps.pos_device.pos.api.v1.serializers.device import DeviceSerializer
from apps.herd.pos.api.v1.serializers import RancherSerializer
@@ -57,6 +59,11 @@ class InventoryEntrySerializer(serializers.ModelSerializer):
'id': instance.distribution.quota.product.id,
}
representation['pricing'] = {
'brokers_info': quota_brokers_value(instance.distribution.quota),
'pricing_attributes': quota_attribute_value(instance.distribution.quota)
}
if 'rancher' in self.context.keys():
# rancher herd & live stock statistics
representation['rancher_statistics'] = get_rancher_statistics(self.context['rancher'])