From 8571ef0c68f6900b8ffdb1cb18a5aaf4316d0b13 Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Thu, 25 Sep 2025 12:16:49 +0330 Subject: [PATCH] change incentive plans total weight quota with rancher incentive plan - change id to rancher plan and add incentive plan id --- apps/herd/models.py | 6 +- apps/herd/services/services.py | 16 +++++- apps/product/migrations/0078_quotausage.py | 40 ++++++++++++++ apps/product/models.py | 64 +++++++++++++--------- apps/product/services/services.py | 3 +- apps/warehouse/pos/api/v1/serializers.py | 6 +- 6 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 apps/product/migrations/0078_quotausage.py diff --git a/apps/herd/models.py b/apps/herd/models.py index 33f1e94..13653df 100644 --- a/apps/herd/models.py +++ b/apps/herd/models.py @@ -1,5 +1,6 @@ -from apps.core.models import BaseModel from apps.authentication import models as auth_models +from apps.core.models import BaseModel + from django.db import models @@ -136,5 +137,4 @@ class Rancher(BaseModel): return f'rancher: {self.first_name} {self.last_name}' def save(self, *args, **kwargs): - return super(Rancher, self).save(*args, **kwargs) - + return super(Rancher, self).save(*args, **kwargs) \ No newline at end of file diff --git a/apps/herd/services/services.py b/apps/herd/services/services.py index 1275de3..2f167f1 100644 --- a/apps/herd/services/services.py +++ b/apps/herd/services/services.py @@ -58,7 +58,8 @@ def rancher_quota_weight(rancher, inventory_entry: InventoryEntry = None, distri total_weight = 0 merged = {} - for item in allocations + incentive_plans: # noqa + # calculate quota base weight by livestock type & base total weight + for item in allocations: # noqa if item.livestock_type: animal_type_fa = item.livestock_type.name animal_type_en = item.livestock_type.en_name @@ -77,6 +78,19 @@ def rancher_quota_weight(rancher, inventory_entry: InventoryEntry = None, distri else: merged[animal_type_en]['weight'] += weight + # calculate rancher incentive plans weight by livestock type & add it to total_weight + for item in incentive_plans: + rancher_plans = item.incentive_plan.rancher_plans.select_related( + 'rancher', + 'livestock_type' + ).filter(rancher=rancher, livestock_type=item.livestock_type) + if rancher_plans: + # multiple count of rancher livestock on incentive plan & + # incentive plan quantity by this livestock type + rancher_plan_weight = rancher_plans.first().allowed_quantity * item.quantity_kg + total_weight += rancher_plan_weight + print(total_weight) + return { "total_weight": total_weight, "remaining_weight": 20, diff --git a/apps/product/migrations/0078_quotausage.py b/apps/product/migrations/0078_quotausage.py new file mode 100644 index 0000000..d7dca2f --- /dev/null +++ b/apps/product/migrations/0078_quotausage.py @@ -0,0 +1,40 @@ +# Generated by Django 5.0 on 2025-09-24 11:56 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('herd', '0018_rancher_dhi_state'), + ('livestock', '0015_livestocktype_en_name_alter_livestocktype_name'), + ('product', '0077_quota_pos_sale_type'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='QuotaUsage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('creator_info', models.CharField(max_length=100, null=True)), + ('modifier_info', models.CharField(max_length=100, null=True)), + ('trash', models.BooleanField(default=False)), + ('count', models.PositiveIntegerField()), + ('base_quota_used', models.DecimalField(decimal_places=2, default=0, max_digits=12)), + ('incentive_quota_used', models.DecimalField(decimal_places=2, default=0, max_digits=12)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)), + ('incentive_plan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='product.incentiveplan')), + ('livestock_type', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='usages', to='livestock.livestocktype')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('rancher', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='usages', to='herd.rancher')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/product/models.py b/apps/product/models.py index ae84f8c..68ed543 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -1,5 +1,4 @@ from apps.authentication.models import OrganizationType, Organization -# from apps.warehouse.models import InventoryQuotaSaleItem from django.contrib.postgres.fields import ArrayField from simple_history.models import HistoricalRecords from apps.authorization.models import UserRelations @@ -9,7 +8,6 @@ from apps.herd.models import Rancher from datetime import datetime from django.db import models import jdatetime -# import datetime class LivestockGroup(models.TextChoices): @@ -470,29 +468,45 @@ class QuotaStats(BaseModel): return super(QuotaStats, self).save(*args, **kwargs) -# class RancherQuotaUsage(BaseModel): -# # transaction = models.ForeignKey("InventoryQuotaSaleTransaction", on_delete=models.CASCADE) -# rancher = models.ForeignKey( -# Rancher, -# on_delete=models.CASCADE, -# related_name='usages', -# null=True -# ) -# livestock_type = models.ForeignKey( -# "LiveStockType", -# on_delete=models.CASCADE, -# related_name='usages', -# null=True -# ) -# count = models.PositiveIntegerField() # چند رأس دام از این نوع -# base_quota_used = models.DecimalField(max_digits=12, decimal_places=2, default=0) -# incentive_quota_used = models.DecimalField(max_digits=12, decimal_places=2, default=0) -# incentive_plan = models.ForeignKey( -# "IncentivePlan", -# null=True, -# blank=True, -# on_delete=models.SET_NULL -# ) +class QuotaUsage(BaseModel): + distribution = models.ForeignKey( + "QuotaDistribution", + on_delete=models.CASCADE, + related_name='usages', + null=True + ) + rancher = models.ForeignKey( + Rancher, + on_delete=models.CASCADE, + related_name='usages', + null=True + ) + livestock_type = models.ForeignKey( + LiveStockType, + on_delete=models.CASCADE, + related_name='usages', + null=True + ) + incentive_plan = models.ForeignKey( + IncentivePlan, + null=True, + blank=True, + on_delete=models.SET_NULL + ) + count = models.PositiveIntegerField() + base_quota_used = models.DecimalField(max_digits=12, decimal_places=2, default=0) + incentive_quota_used = models.DecimalField(max_digits=12, decimal_places=2, default=0) + usage_type_choices = ( + ('base', 'BASE'), + ('incentive', 'INCENTIVE'), + ) + usage_type = models.CharField(max_length=150, choices=usage_type_choices, null=True) + + def __str__(self): + return f'rancher: {self.rancher.ranching_farm} - plan: {self.incentive_plan.name}' + + def save(self, *args, **kwargs): + return super(QuotaUsage, self).save(*args, **kwargs) class QuotaIncentiveAssignment(BaseModel): diff --git a/apps/product/services/services.py b/apps/product/services/services.py index 4c7cdd9..949c987 100644 --- a/apps/product/services/services.py +++ b/apps/product/services/services.py @@ -47,13 +47,14 @@ def quota_incentive_plans_info(quota: Quota, rancher: Rancher) -> typing.Any: ).filter(rancher=rancher, livestock_type=plan.livestock_type) incentive_plans_data = { - 'id': plan.incentive_plan.id, + 'id': rancher_plan.first().allowed_quantity, 'name': plan.incentive_plan.name, 'livestock_type': plan.livestock_type.name if plan.livestock_type else "", 'livestock_type_en': plan.livestock_type.en_name if plan.livestock_type else "", 'livestock_weight_type': plan.livestock_type.weight_type if plan.livestock_type else "", 'quantity_kg': plan.quantity_kg, 'rancher_license_count': rancher_plan.first().allowed_quantity if rancher_plan else 0, + "incentive_plan": plan.incentive_plan.id } incentive_plans_list.append(incentive_plans_data) diff --git a/apps/warehouse/pos/api/v1/serializers.py b/apps/warehouse/pos/api/v1/serializers.py index a32dba1..eaf8304 100644 --- a/apps/warehouse/pos/api/v1/serializers.py +++ b/apps/warehouse/pos/api/v1/serializers.py @@ -143,12 +143,12 @@ class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer): # get product by type gov_product = item_data.pop('gov_product') if 'gov_product' in item_data.keys() else None free_product = item_data.pop('free_product') if 'free_product' in item_data.keys() else None + distribution = QuotaDistribution.objects.get(id=item_data.pop('quota_distribution')) + # create item for transaction item = warehouse_models.InventoryQuotaSaleItem.objects.create( transaction=transaction, - quota_distribution=QuotaDistribution.objects.get( - id=item_data.pop('quota_distribution') - ), + quota_distribution=distribution, gov_product=Product.objects.get( id=gov_product ) if Product.objects.filter(id=gov_product).exists() else None,