From e8f4c77e9fdd41d491f5a06e02d0ecabe9820d1f Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Sat, 20 Sep 2025 12:41:24 +0330 Subject: [PATCH] free sale of inventory entry system deployment - add dhi state to rancher --- apps/herd/models.py | 1 + apps/product/exceptions.py | 8 + ...calquotadistribution_free_sale_and_more.py | 33 +++ apps/product/signals.py | 228 +++++++++--------- apps/warehouse/migrations/0024_extrasale.py | 37 +++ .../0025_extrasale_transaction_and_more.py | 24 ++ .../migrations/0026_alter_extrasale_weight.py | 18 ++ .../migrations/0027_extrasale_sale_item.py | 19 ++ apps/warehouse/models.py | 37 ++- apps/warehouse/pos/api/v1/serializers.py | 98 ++++---- apps/warehouse/services/services.py | 40 ++- apps/warehouse/signals.py | 5 + 12 files changed, 390 insertions(+), 158 deletions(-) create mode 100644 apps/product/migrations/0073_historicalquotadistribution_free_sale_and_more.py create mode 100644 apps/warehouse/migrations/0024_extrasale.py create mode 100644 apps/warehouse/migrations/0025_extrasale_transaction_and_more.py create mode 100644 apps/warehouse/migrations/0026_alter_extrasale_weight.py create mode 100644 apps/warehouse/migrations/0027_extrasale_sale_item.py diff --git a/apps/herd/models.py b/apps/herd/models.py index d0ddb6a..33f1e94 100644 --- a/apps/herd/models.py +++ b/apps/herd/models.py @@ -130,6 +130,7 @@ class Rancher(BaseModel): ignore_purchase_limit = models.BooleanField( default=False, help_text="if its true rancher has not buy limitations" ) + dhi_state = models.BooleanField(default=False) def __str__(self): return f'rancher: {self.first_name} {self.last_name}' diff --git a/apps/product/exceptions.py b/apps/product/exceptions.py index bd1f0fc..b898a50 100644 --- a/apps/product/exceptions.py +++ b/apps/product/exceptions.py @@ -10,6 +10,14 @@ class QuotaWeightException(APIException): default_code = 'error' +class DistributionWeightException(APIException): + """ if sale transaction weight is more than distribution weight """ + + status_code = status.HTTP_400_BAD_REQUEST + default_detail = 'مقدار وزن وارد شده بیشتر از وزن سهمیه توزیع شده میباشد.' # noqa + default_code = 'error' + + class QuotaClosedException(APIException): """ if quota is closed, operations can not be done """ diff --git a/apps/product/migrations/0073_historicalquotadistribution_free_sale_and_more.py b/apps/product/migrations/0073_historicalquotadistribution_free_sale_and_more.py new file mode 100644 index 0000000..3c7f06b --- /dev/null +++ b/apps/product/migrations/0073_historicalquotadistribution_free_sale_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0 on 2025-09-16 07:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0072_alter_quota_base_price_cooperative_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='historicalquotadistribution', + name='free_sale', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='historicalquotadistribution', + name='pre_sale', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='quotadistribution', + name='free_sale', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='quotadistribution', + name='pre_sale', + field=models.BooleanField(default=False), + ), + ] diff --git a/apps/product/signals.py b/apps/product/signals.py index 4384315..d82cce6 100644 --- a/apps/product/signals.py +++ b/apps/product/signals.py @@ -1,8 +1,10 @@ from django.db.models import Sum, Q from django.db.models.signals import post_save, post_delete +from django.contrib.auth.models import AnonymousUser from common.helpers import get_organization_by_user from rest_framework.exceptions import APIException from django.dispatch import receiver +from apps.authentication.models import User from .models import ( QuotaDistribution, Quota, @@ -72,134 +74,136 @@ def update_quota_remaining(sender, instance, **kwargs): def update_product_stats(instance: Product, distribution: QuotaDistribution = None): """ update all stats of product """ - organization = get_organization_by_user(get_current_user()) - - if ProductStats.objects.filter( - organization=organization, - product=instance, - sale_unit=distribution.quota.sale_unit.unit - ): - stat = instance.stats.get( - organization=organization, - product=instance, - sale_unit=distribution.quota.sale_unit.unit - ) - else: - stat = ProductStats.objects.create( - product=instance, - organization=organization, - sale_unit=distribution.quota.sale_unit.unit - ) - - # number of quotas - - quota = Quota.objects.filter( - Q( - distributions_assigned__in=QuotaDistribution.objects.filter( - Q(assigned_organization=organization) | - Q(assigner_organization=organization) & - Q(parent_distribution__isnull=True) + user = get_current_user() # get user object + if not isinstance(user, AnonymousUser): + organization = get_organization_by_user(user) + if ProductStats.objects.filter( + organization=organization, + product=instance, + sale_unit=distribution.quota.sale_unit.unit + ): + stat = instance.stats.get( + organization=organization, + product=instance, + sale_unit=distribution.quota.sale_unit.unit + ) + else: + stat = ProductStats.objects.create( + product=instance, + organization=organization, + sale_unit=distribution.quota.sale_unit.unit ) - ) | - Q(registerer_organization=organization), - product=instance - ).distinct() - quotas_count = quota.count() # noqa + # number of quotas - total_quotas_weight = quota.aggregate( # noqa - total=models.Sum('quota_weight') - )['total'] or 0 + quota = Quota.objects.filter( + Q( + distributions_assigned__in=QuotaDistribution.objects.filter( + Q(assigned_organization=organization) | + Q(assigner_organization=organization) & + Q(parent_distribution__isnull=True) - # total weight of product that assigned in quota - active_quotas_weight = quota.filter(is_closed=False).aggregate( - total=models.Sum('quota_weight') - )['total'] or 0 + ) + ) | + Q(registerer_organization=organization), + product=instance + ).distinct() - closed_quotas_weight = quota.filter(is_closed=True).aggregate( # noqa - total=models.Sum('quota_weight') - )['total'] or 0 + quotas_count = quota.count() # noqa - # total remaining weight of product quotas - total_remaining_quotas_weight = quota.filter(is_closed=False).aggregate( # noqa - total=models.Sum('remaining_weight') - )['total'] or 0 + total_quotas_weight = quota.aggregate( # noqa + total=models.Sum('quota_weight') + )['total'] or 0 - received_distribution_weight = QuotaDistribution.objects.filter( - quota__product_id=instance.id, - quota__is_closed=False, - quota__sale_unit=distribution.quota.sale_unit, - assigned_organization=organization, - parent_distribution__isnull=True - ) + # total weight of product that assigned in quota + active_quotas_weight = quota.filter(is_closed=False).aggregate( + total=models.Sum('quota_weight') + )['total'] or 0 - received_distribution_number = received_distribution_weight.count() + closed_quotas_weight = quota.filter(is_closed=True).aggregate( # noqa + total=models.Sum('quota_weight') + )['total'] or 0 - received_distribution_weight = received_distribution_weight.aggregate( - total_weight=models.Sum('weight') - )['total_weight'] or 0 + # total remaining weight of product quotas + total_remaining_quotas_weight = quota.filter(is_closed=False).aggregate( # noqa + total=models.Sum('remaining_weight') + )['total'] or 0 - # product total distributed weight from quota - given_distribution_weight = QuotaDistribution.objects.filter( - quota__product_id=instance.id, - quota__is_closed=False, - quota__sale_unit=distribution.quota.sale_unit, - assigner_organization=organization, - parent_distribution__isnull=True - ) + received_distribution_weight = QuotaDistribution.objects.filter( + quota__product_id=instance.id, + quota__is_closed=False, + quota__sale_unit=distribution.quota.sale_unit, + assigned_organization=organization, + parent_distribution__isnull=True + ) - given_distribution_number = given_distribution_weight.count() - given_distribution_weight = given_distribution_weight.aggregate( - total_weight=models.Sum('weight') - )['total_weight'] or 0 + received_distribution_number = received_distribution_weight.count() - if received_distribution_weight > 0: - distribution_weight_balance = received_distribution_weight - given_distribution_weight - else: - distribution_weight_balance = given_distribution_weight + received_distribution_weight = received_distribution_weight.aggregate( + total_weight=models.Sum('weight') + )['total_weight'] or 0 - # total sold of product from quota - total_sold = QuotaDistribution.objects.filter( - quota__product_id=instance.id, - quota__is_closed=False, - quota__sale_unit=distribution.quota.sale_unit, - assigned_organization=organization - ).aggregate(total_sold=models.Sum('been_sold'))['total_sold'] or 0 + # product total distributed weight from quota + given_distribution_weight = QuotaDistribution.objects.filter( + quota__product_id=instance.id, + quota__is_closed=False, + quota__sale_unit=distribution.quota.sale_unit, + assigner_organization=organization, + parent_distribution__isnull=True + ) - # total entry from product to inventory - total_warehouse_entry = QuotaDistribution.objects.filter( - quota__product_id=instance.id, - quota__is_closed=False, - quota__sale_unit=distribution.quota.sale_unit, - assigned_organization=organization - ).aggregate(total_entry=models.Sum('warehouse_entry'))['total_entry'] or 0 + given_distribution_number = given_distribution_weight.count() + given_distribution_weight = given_distribution_weight.aggregate( + total_weight=models.Sum('weight') + )['total_weight'] or 0 - stat.quotas_number = quotas_count - stat.active_quotas_weight = active_quotas_weight - stat.closed_quotas_weight = closed_quotas_weight - stat.total_quota_weight = total_quotas_weight - stat.total_quota_remaining = total_remaining_quotas_weight - stat.total_remaining_distribution_weight = distribution_weight_balance - stat.received_distribution_weight = received_distribution_weight - stat.given_distribution_weight = given_distribution_weight - stat.received_distribution_number = received_distribution_number - stat.given_distribution_number = given_distribution_number - stat.total_warehouse_entry = total_warehouse_entry - stat.total_sold = total_sold - stat.save(update_fields=[ - "quotas_number", - "active_quotas_weight", - "closed_quotas_weight", - "total_quota_weight", - "total_quota_remaining", - "total_remaining_distribution_weight", - "received_distribution_weight", - "given_distribution_weight", - "received_distribution_number", - "total_warehouse_entry", - "total_sold", - ]) + if received_distribution_weight > 0: + distribution_weight_balance = received_distribution_weight - given_distribution_weight + else: + distribution_weight_balance = given_distribution_weight + + # total sold of product from quota + total_sold = QuotaDistribution.objects.filter( + quota__product_id=instance.id, + quota__is_closed=False, + quota__sale_unit=distribution.quota.sale_unit, + assigned_organization=organization + ).aggregate(total_sold=models.Sum('been_sold'))['total_sold'] or 0 + + # total entry from product to inventory + total_warehouse_entry = QuotaDistribution.objects.filter( + quota__product_id=instance.id, + quota__is_closed=False, + quota__sale_unit=distribution.quota.sale_unit, + assigned_organization=organization + ).aggregate(total_entry=models.Sum('warehouse_entry'))['total_entry'] or 0 + + stat.quotas_number = quotas_count + stat.active_quotas_weight = active_quotas_weight + stat.closed_quotas_weight = closed_quotas_weight + stat.total_quota_weight = total_quotas_weight + stat.total_quota_remaining = total_remaining_quotas_weight + stat.total_remaining_distribution_weight = distribution_weight_balance + stat.received_distribution_weight = received_distribution_weight + stat.given_distribution_weight = given_distribution_weight + stat.received_distribution_number = received_distribution_number + stat.given_distribution_number = given_distribution_number + stat.total_warehouse_entry = total_warehouse_entry + stat.total_sold = total_sold + stat.save(update_fields=[ + "quotas_number", + "active_quotas_weight", + "closed_quotas_weight", + "total_quota_weight", + "total_quota_remaining", + "total_remaining_distribution_weight", + "received_distribution_weight", + "given_distribution_weight", + "received_distribution_number", + "total_warehouse_entry", + "total_sold", + ]) def update_quota_stats(instance: Quota): diff --git a/apps/warehouse/migrations/0024_extrasale.py b/apps/warehouse/migrations/0024_extrasale.py new file mode 100644 index 0000000..c851861 --- /dev/null +++ b/apps/warehouse/migrations/0024_extrasale.py @@ -0,0 +1,37 @@ +# Generated by Django 5.0 on 2025-09-16 07:41 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0036_organization_phone'), + ('product', '0073_historicalquotadistribution_free_sale_and_more'), + ('warehouse', '0023_inventoryquotasaletransaction_inventory_entry_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='ExtraSale', + 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)), + ('weight', models.PositiveBigIntegerField(default=0)), + ('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)), + ('distribution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='extra_sales', to='product.quotadistribution')), + ('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)), + ('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='extra_sales', to='authentication.organization')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/warehouse/migrations/0025_extrasale_transaction_and_more.py b/apps/warehouse/migrations/0025_extrasale_transaction_and_more.py new file mode 100644 index 0000000..5020e49 --- /dev/null +++ b/apps/warehouse/migrations/0025_extrasale_transaction_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0 on 2025-09-17 05:06 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('warehouse', '0024_extrasale'), + ] + + operations = [ + migrations.AddField( + model_name='extrasale', + name='transaction', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transactions', to='warehouse.inventoryquotasaletransaction'), + ), + migrations.AddField( + model_name='inventoryquotasaleitem', + name='is_extra', + field=models.BooleanField(default=False), + ), + ] diff --git a/apps/warehouse/migrations/0026_alter_extrasale_weight.py b/apps/warehouse/migrations/0026_alter_extrasale_weight.py new file mode 100644 index 0000000..993d1ef --- /dev/null +++ b/apps/warehouse/migrations/0026_alter_extrasale_weight.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0 on 2025-09-17 05:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('warehouse', '0025_extrasale_transaction_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='extrasale', + name='weight', + field=models.IntegerField(default=0), + ), + ] diff --git a/apps/warehouse/migrations/0027_extrasale_sale_item.py b/apps/warehouse/migrations/0027_extrasale_sale_item.py new file mode 100644 index 0000000..82de4e7 --- /dev/null +++ b/apps/warehouse/migrations/0027_extrasale_sale_item.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0 on 2025-09-17 10:41 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('warehouse', '0026_alter_extrasale_weight'), + ] + + operations = [ + migrations.AddField( + model_name='extrasale', + name='sale_item', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_items', to='warehouse.inventoryquotasaleitem'), + ), + ] diff --git a/apps/warehouse/models.py b/apps/warehouse/models.py index 5699924..3a66fff 100644 --- a/apps/warehouse/models.py +++ b/apps/warehouse/models.py @@ -1,7 +1,7 @@ import string import random +from apps.authentication.models import User, Organization from apps.product import models as product_models -from apps.authentication.models import User from apps.pos_device.models import Device from apps.herd.models import Rancher from apps.core.models import BaseModel @@ -149,9 +149,44 @@ class InventoryQuotaSaleItem(BaseModel): weight = models.PositiveBigIntegerField(default=0) unit_price = models.PositiveBigIntegerField(default=0) total_price = models.PositiveBigIntegerField(default=0) + is_extra = models.BooleanField(default=False) def __str__(self): return f'Item {self.product} - {self.weight} Kg - {self.total_price}' def save(self, *args, **kwargs): return super(InventoryQuotaSaleItem, self).save(*args, **kwargs) + + +class ExtraSale(BaseModel): + organization = models.ForeignKey( + Organization, + on_delete=models.CASCADE, + related_name='extra_sales', + null=True + ) + distribution = models.ForeignKey( + product_models.QuotaDistribution, + on_delete=models.CASCADE, + related_name='extra_sales', + null=True + ) + transaction = models.ForeignKey( + InventoryQuotaSaleTransaction, + on_delete=models.CASCADE, + related_name='transactions', + null=True + ) + sale_item = models.ForeignKey( + InventoryQuotaSaleItem, + on_delete=models.CASCADE, + related_name='sale_items', + null=True + ) + weight = models.IntegerField(default=0) + + def __str__(self): + return f'Extra Sale on {self.organization.name} - {self.distribution.distribution_id}' + + def save(self, *args, **kwargs): + return super(ExtraSale, self).save(*args, **kwargs) diff --git a/apps/warehouse/pos/api/v1/serializers.py b/apps/warehouse/pos/api/v1/serializers.py index cf5f9c4..c45e3a2 100644 --- a/apps/warehouse/pos/api/v1/serializers.py +++ b/apps/warehouse/pos/api/v1/serializers.py @@ -7,10 +7,13 @@ from apps.product.services.services import ( ) from apps.pos_device.services.services import pos_organizations_sharing_information from apps.pos_device.pos.api.v1.serializers.device import DeviceSerializer +from apps.product.exceptions import DistributionWeightException +from apps.warehouse.services.services import create_extra_sale from apps.herd.pos.api.v1.serializers import RancherSerializer from apps.product.models import QuotaDistribution, Product from apps.warehouse import models as warehouse_models from apps.core.models import SystemConfig +from django.db.transaction import atomic from apps.warehouse.exceptions import ( TotalInventorySaleException ) @@ -99,6 +102,7 @@ class InventoryEntrySerializer(serializers.ModelSerializer): class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer): rancher_national_code = serializers.CharField(max_length=50, required=False) + class Meta: # noqa model = warehouse_models.InventoryQuotaSaleTransaction fields = '__all__' @@ -106,60 +110,66 @@ class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer): def create(self, validated_data): items_data = self.context['request'].data['items'] + with atomic(): + # get rancher with national code + rancher = Rancher.objects.get(national_code=validated_data.pop('rancher_national_code')) + validated_data.update({'rancher': rancher}) - # get rancher with national code - rancher = Rancher.objects.get(national_code=validated_data.pop('rancher_national_code')) - validated_data.update({'rancher': rancher}) - - # if transaction exists, update transaction status - transaction = self.Meta.model.objects.filter( - transaction_id=validated_data.get('transaction_id') - ) - if transaction.exists(): - obj = transaction.first() - obj.transaction_status = validated_data.get('transaction_status') - obj.save(update_fields=['transaction_status']) - - return obj - - # create transaction record - transaction = warehouse_models.InventoryQuotaSaleTransaction.objects.create( - **validated_data - ) - - # calculate total price of product items in shopping cart - total_price = 0 - for item_data in items_data: - item = warehouse_models.InventoryQuotaSaleItem.objects.create( - transaction=transaction, - quota_distribution=QuotaDistribution.objects.get( - id=item_data.pop('quota_distribution') - ), - product=Product.objects.get( - id=item_data.pop('product') - ), - **item_data + # if transaction exists, update transaction status + transaction = self.Meta.model.objects.filter( + transaction_id=validated_data.get('transaction_id') ) - total_price += item.total_price - transaction.transaction_price = total_price - transaction.save() + if transaction.exists(): + obj = transaction.first() + obj.transaction_status = validated_data.get('transaction_status') + obj.save(update_fields=['transaction_status']) - return transaction + return obj + + # create transaction record + transaction = warehouse_models.InventoryQuotaSaleTransaction.objects.create( + **validated_data + ) + + # calculate total price of product items in shopping cart + total_price = 0 + for item_data in items_data: + item = warehouse_models.InventoryQuotaSaleItem.objects.create( + transaction=transaction, + quota_distribution=QuotaDistribution.objects.get( + id=item_data.pop('quota_distribution') + ), + product=Product.objects.get( + id=item_data.pop('product') + ), + **item_data + ) + total_price += item.total_price + + # create extra sale + create_extra_sale(transaction=transaction, sale_item=item) + + transaction.transaction_price = total_price + transaction.save() + + return transaction def validate(self, attrs): """ validate total inventory sale should be fewer than - inventory entry from distribution + distribution weight """ - if 'quota_distribution' in attrs.keys(): - distribution = attrs['quota_distribution'] - total_sale_weight = distribution.inventory_sales.aggregate( - total=models.Sum('weight') - )['total'] or 0 + items = self.context['request'].data['items'] + for item in items: + if 'quota_distribution' in item.keys(): + distribution = QuotaDistribution.objects.get(id=item.get('quota_distribution')) + total_sale_weight = distribution.sale_items.aggregate( + total=models.Sum('weight') + )['total'] or 0 - if total_sale_weight + attrs['weight'] > distribution.warehouse_balance: - raise TotalInventorySaleException() + if total_sale_weight + attrs['weight'] > distribution.weight: + raise DistributionWeightException() return attrs diff --git a/apps/warehouse/services/services.py b/apps/warehouse/services/services.py index 7a235fd..8f6bb18 100644 --- a/apps/warehouse/services/services.py +++ b/apps/warehouse/services/services.py @@ -1,8 +1,14 @@ from apps.herd.services.services import rancher_quota_weight, get_rancher_statistics -from apps.warehouse.models import InventoryEntry, InventoryQuotaSaleTransaction +from apps.warehouse.models import ( + InventoryEntry, + InventoryQuotaSaleTransaction, + InventoryQuotaSaleItem, + ExtraSale +) from apps.product.models import QuotaDistribution from apps.core.models import SystemConfig from django.db.models import Sum +import typing def get_total_sold(rancher, inventory_entry: InventoryEntry = None, distribution: QuotaDistribution = None): @@ -53,3 +59,35 @@ def can_buy_from_inventory(rancher, inventory_entry: InventoryEntry = None, dist total_sold = get_total_sold(inventory_entry, rancher) return total_sold < total_allowed + + +def create_extra_sale( + sale_item: InventoryQuotaSaleItem, + transaction: InventoryQuotaSaleTransaction +) -> typing.Any: + """ + :param sale_item + :param transaction + create extra sale for items that weight are + more than distribution warehouse balance + """ + + if sale_item.quota_distribution.free_sale: + if sale_item.weight > sale_item.quota_distribution.warehouse_balance: + # calculate extra weight between item weight and warehouse balance + extra_weight = sale_item.weight - sale_item.quota_distribution.warehouse_balance + extra_sale = ExtraSale.objects.create( # create extra sale + transaction=transaction, + sale_item=sale_item, + distribution=sale_item.quota_distribution, + weight=extra_weight + ) + + # set distribution warehouse balance to 0 because we have extra sale + sale_item.quota_distribution.warehouse_balance = 0 + sale_item.quota_distribution.save() + + return extra_sale + pass + pass + diff --git a/apps/warehouse/signals.py b/apps/warehouse/signals.py index 47600cc..4f7817e 100644 --- a/apps/warehouse/signals.py +++ b/apps/warehouse/signals.py @@ -21,6 +21,11 @@ def warehouse_sold_and_balance(quota_distribution: QuotaDistribution): quota_distribution.been_sold = total_sold quota_distribution.warehouse_balance = quota_distribution.warehouse_entry - total_sold + if quota_distribution.warehouse_balance >= 0: + extra_sales = quota_distribution.extra_sales.all() + total_extra_sales_weight = extra_sales.aggregate(total=Sum('weight'))['total'] or 0 + if total_extra_sales_weight != 0: + quota_distribution.warehouse_balance = quota_distribution.warehouse_entry - total_extra_sales_weight quota_distribution.save(update_fields=['been_sold', 'warehouse_balance'])