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.livestock.models import LiveStock
from apps.warehouse.models import InventoryEntry from apps.warehouse.models import InventoryEntry
from apps.product.models import Quota from apps.product.models import Quota
from django.db.models import Count, Q
import typing import typing
def get_rancher_statistics(rancher: Rancher = None) -> typing.Any: def get_rancher_statistics(rancher: Rancher = None) -> typing.Any:
""" get statistics of a rancher """ # noqa """ get statistics of a rancher """ # noqa
herds = rancher.herd.all() # noqa livestocks = LiveStock.objects.filter(herd__rancher=rancher) # noqa
herd_count = herds.count()
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() return stats
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,
}
def rancher_quota_weight(rancher, inventory_entry: InventoryEntry): 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 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) livestock_counts = get_rancher_statistics(rancher)
total_weight = 0 total_weight = 0
alloc_details = {} merged = {}
plan_details = {}
result_list = []
# list of quota allocations, get allocations weight on any animal type for item in allocations + incentive_plans: # noqa
for alloc in allocations: # noqa if item.livestock_type:
if alloc.livestock_type: animal_type = item.livestock_type.name
animal_type = alloc.livestock_type.name per_head = item.quantity_kg
per_head = alloc.quantity_kg
count = livestock_counts.get(live_stock_meta.get(animal_type), 0) count = livestock_counts.get(live_stock_meta.get(animal_type), 0)
weight = per_head * count weight = per_head * count
alloc_details[animal_type] = {"weight": weight, "type": alloc.livestock_type.weight_type}
total_weight += weight total_weight += weight
# list of quota incentive plans, get plans weight on any animal type if animal_type not in merged:
incentive_plans = quota.incentive_assignments.all() merged[animal_type] = {
for plan in incentive_plans: # noqa "weight": weight,
if plan.livestock_type: "type": item.livestock_type.weight_type
animal_type = plan.livestock_type.name }
per_head = plan.quantity_kg else:
count = livestock_counts.get(live_stock_meta.get(animal_type), 0) merged[animal_type]['weight'] += weight
weight = per_head * count return {
plan_details[animal_type] = {"weight": weight, "type": plan.livestock_type.weight_type} "total_weight": total_weight,
total_weight += weight "by_type": [{
# 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({
"name": key, "name": key,
"weight": alloc_weight + plan_weight, "weight": value['weight'],
"type": animal_type "type": value['type']
}) }for key, value in merged.items()]
}
result_details['by_type'] = result_list
return result_details

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 from apps.product.models import Product, Broker, QuotaBrokerValue
import string
from apps.authentication.models import Organization
from apps.product.models import Broker
from apps.product.models import Product
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from apps.authorization.models import UserRelations from apps.authorization.models import UserRelations
from apps.authentication.models import Organization
from apps.core.models import BaseModel from apps.core.models import BaseModel
from django.db import models from django.db import models
import random
import string
class ProviderCompany(BaseModel): class ProviderCompany(BaseModel):
@@ -258,6 +256,18 @@ class StakeHolders(BaseModel):
related_name='pos_stake_holders', related_name='pos_stake_holders',
null=True 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) share_percent = models.FloatField(default=0)
default = models.BooleanField(default=False) default = models.BooleanField(default=False)
@@ -295,29 +305,3 @@ class POSFreeProducts(BaseModel):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
return super(POSFreeProducts, self).save(*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 ).data
return representation 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', device_views.DeviceViewSet, basename='device')
router.register(r'device_assignment', device_views.DeviceAssignmentViewSet, basename='device_assignment') 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'stake_holders', device_views.StakeHoldersViewSet, basename='stake_holders')
router.register(r'broker_stake_assign', device_views.BrokerStakeHolderAssignViewSet, basename='broker_stake_assign')
urlpatterns = [ urlpatterns = [
path('v1/pos/', include(router.urls)) 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) serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data) 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: def quota_live_stock_allocation_info(quota: Quota) -> typing.Any:
""" information of quota live stock allocations """ """ information of quota live stock allocations """
allocations = quota.livestock_allocations.filter(quota=quota) allocations = quota.livestock_allocations.select_related('livestock_type')
if allocations: if allocations:
allocations_list = [{ 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: def quota_incentive_plans_info(quota: Quota) -> typing.Any:
""" information of quota incentive plans """ """ 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: if incentive_plans:
incentive_plans_list = [{ incentive_plans_list = [{
@@ -48,3 +48,31 @@ def quota_incentive_plans_info(quota: Quota) -> typing.Any:
} for plan in incentive_plans] } for plan in incentive_plans]
return incentive_plans_list 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.herd.services.services import get_rancher_statistics, rancher_quota_weight
from apps.product.services.services import ( from apps.product.services.services import (
quota_live_stock_allocation_info, 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.pos_device.pos.api.v1.serializers.device import DeviceSerializer
from apps.herd.pos.api.v1.serializers import RancherSerializer from apps.herd.pos.api.v1.serializers import RancherSerializer
@@ -57,6 +59,11 @@ class InventoryEntrySerializer(serializers.ModelSerializer):
'id': instance.distribution.quota.product.id, '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(): if 'rancher' in self.context.keys():
# rancher herd & live stock statistics # rancher herd & live stock statistics
representation['rancher_statistics'] = get_rancher_statistics(self.context['rancher']) representation['rancher_statistics'] = get_rancher_statistics(self.context['rancher'])