fix - change transaction base from distribution to quota stat
This commit is contained in:
23
apps/herd/management/commands/fix_herd_status_of_rancher.py
Normal file
23
apps/herd/management/commands/fix_herd_status_of_rancher.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import Count
|
||||
|
||||
from apps.herd.models import Rancher
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Fast update ranchers without herds using bulk updates"
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
self.stdout.write("🔄 Bulk updating ranchers...")
|
||||
|
||||
no_herd_count = Rancher.objects.annotate(
|
||||
herd_count=Count("herd")
|
||||
).filter(herd_count=0).update(without_herd=True)
|
||||
|
||||
has_herd_count = Rancher.objects.annotate(
|
||||
herd_count=Count("herd")
|
||||
).filter(herd_count__gt=0).update(without_herd=False)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f"✅ Done! {no_herd_count} set to True, {has_herd_count} set to False"
|
||||
))
|
||||
@@ -167,6 +167,7 @@ def rancher_quota_weight(
|
||||
"total_weight": total_weight,
|
||||
"remaining_weight": remaining_weight,
|
||||
'free_sale': free_sale,
|
||||
'total_purchase': 0,
|
||||
"rancher_temporary_livestock": rancher.without_herd,
|
||||
"by_type": [{
|
||||
"name": key,
|
||||
|
||||
19
apps/product/migrations/0098_quotausage_quota_stat.py
Normal file
19
apps/product/migrations/0098_quotausage_quota_stat.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0 on 2025-11-26 11:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('product', '0097_organizationquotastats_inventory_entry_balance'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='quotausage',
|
||||
name='quota_stat',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='usages', to='product.organizationquotastats'),
|
||||
),
|
||||
]
|
||||
@@ -580,6 +580,12 @@ class QuotaStats(BaseModel):
|
||||
|
||||
|
||||
class QuotaUsage(BaseModel):
|
||||
quota_stat = models.ForeignKey(
|
||||
'OrganizationQuotaStats',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='usages',
|
||||
null=True
|
||||
)
|
||||
distribution = models.ForeignKey(
|
||||
"QuotaDistribution",
|
||||
on_delete=models.CASCADE,
|
||||
|
||||
@@ -274,7 +274,7 @@ class OrganizationQuotaStatsSerializer(serializers.ModelSerializer):
|
||||
representation['pre_sale'] = instance.quota.pre_sale
|
||||
|
||||
if instance.distributions:
|
||||
representation['distributions'] = [dist.id for dist in instance.distributions.all()]
|
||||
representation['distribution'] = instance.distributions.all().order_by('-create_date').first().id
|
||||
|
||||
if instance.quota:
|
||||
representation['quota'] = {
|
||||
|
||||
@@ -4,6 +4,7 @@ from rest_framework import status
|
||||
from rest_framework import viewsets, filters
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.core.mixins.search_mixin import DynamicSearchMixin
|
||||
@@ -198,6 +199,7 @@ class QuotaLiveStockAgeLimitation(viewsets.ModelViewSet):
|
||||
class OrganizationQuotaStatsViewSet(viewsets.ModelViewSet, DynamicSearchMixin, POSDeviceMixin):
|
||||
queryset = OrganizationQuotaStats.objects.all()
|
||||
serializer_class = quota_serializers.OrganizationQuotaStatsSerializer
|
||||
permission_classes = [AllowAny]
|
||||
filter_backends = [filters.SearchFilter]
|
||||
search_fields = [
|
||||
"quota__registerer_organization__name",
|
||||
|
||||
0
apps/warehouse/pos/api/v2/__init__.py
Normal file
0
apps/warehouse/pos/api/v2/__init__.py
Normal file
148
apps/warehouse/pos/api/v2/api.py
Normal file
148
apps/warehouse/pos/api/v2/api.py
Normal file
@@ -0,0 +1,148 @@
|
||||
from django.db import transaction
|
||||
from rest_framework import status
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.core.mixins.search_mixin import DynamicSearchMixin
|
||||
from apps.herd.models import Rancher
|
||||
from apps.pos_device.mixins.pos_device_mixin import POSDeviceMixin
|
||||
from apps.warehouse import models as warehouse_models
|
||||
from apps.warehouse.pos.api.v2 import serializers as warehouse_serializers
|
||||
from apps.warehouse.services.services import (
|
||||
can_buy_from_inventory
|
||||
)
|
||||
|
||||
|
||||
class InventoryEntryViewSet(viewsets.ModelViewSet, DynamicSearchMixin, POSDeviceMixin):
|
||||
queryset = warehouse_models.InventoryEntry.objects.all()
|
||||
serializer_class = warehouse_serializers.InventoryEntrySerializer
|
||||
permission_classes = [AllowAny]
|
||||
search_fields = [
|
||||
"distribution__distribution_id",
|
||||
"organization__name",
|
||||
"weight",
|
||||
"balance",
|
||||
"lading_number",
|
||||
"is_confirmed",
|
||||
]
|
||||
date_field = "create_date"
|
||||
|
||||
@action(
|
||||
methods=['get'],
|
||||
detail=False,
|
||||
url_path='my_entries',
|
||||
url_name='my_entries',
|
||||
name='my_entries'
|
||||
)
|
||||
def inventory_entries(self, request):
|
||||
""" list of pos inventory entries """
|
||||
|
||||
organization = self.get_device_organization()
|
||||
device = self.get_pos_device()
|
||||
|
||||
entries = self.queryset.filter(organization=organization)
|
||||
queryset = self.filter_query(entries) # return by search param or all objects
|
||||
|
||||
# paginate & response
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True, context={'device': device})
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
@action(
|
||||
methods=['get'],
|
||||
detail=False,
|
||||
url_path='rancher_inventory_entries',
|
||||
url_name='rancher_inventory_entries',
|
||||
name='rancher_inventory_entries'
|
||||
)
|
||||
@transaction.atomic
|
||||
def rancher_inventory_entries(self, request):
|
||||
""" list of inventory entries (quotas) for rancher """
|
||||
|
||||
organization = self.get_device_organization()
|
||||
device = self.get_pos_device()
|
||||
rancher = Rancher.objects.filter(national_code=request.GET['national_code'])
|
||||
entries = self.queryset.filter(organization=organization)
|
||||
|
||||
# check quota inventory entries for rancher
|
||||
available_entries = [
|
||||
entry for entry in entries if (can_buy_from_inventory(rancher.first(), entry) & rancher.exists())
|
||||
]
|
||||
|
||||
# paginate & response
|
||||
page = self.paginate_queryset(available_entries) # noqa
|
||||
if page is not None: # noqa
|
||||
serializer = self.get_serializer(page, many=True, context={'rancher': rancher.first(), 'device': device})
|
||||
# set custom message for paginator
|
||||
if not rancher:
|
||||
self.paginator.set_message("دامدار با کد ملی مد نظر یافت نشد") # noqa
|
||||
elif not available_entries:
|
||||
self.paginator.set_message("دامدار با کد ملی مد نظر سهمیه ایی ندارد") # noqa
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
|
||||
class InventoryQuotaSaleTransactionViewSet(viewsets.ModelViewSet, DynamicSearchMixin, POSDeviceMixin):
|
||||
queryset = warehouse_models.InventoryQuotaSaleTransaction.objects.all().prefetch_related('items')
|
||||
serializer_class = warehouse_serializers.InventoryQuotaSaleTransactionSerializer
|
||||
permission_classes = [AllowAny]
|
||||
search_fields = [
|
||||
"rancher__union_name",
|
||||
"rancher__union_code",
|
||||
"rancher__first_name",
|
||||
"rancher__last_name",
|
||||
"rancher__national_code",
|
||||
"pos_device__device_identity",
|
||||
"pos_device__serial",
|
||||
"transaction_id",
|
||||
"seller_organization__name",
|
||||
"quota_distribution__distribution_id",
|
||||
"inventory_entry__distribution__distribution_id",
|
||||
"transaction_status",
|
||||
"delivery_address",
|
||||
"product_type",
|
||||
]
|
||||
date_field = "create_date"
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
""" pos transactions list """
|
||||
|
||||
# get device object
|
||||
device = self.get_pos_device()
|
||||
|
||||
queryset = self.queryset.filter(pos_device=device).order_by('-modify_date')
|
||||
queryset = self.filter_query(queryset)
|
||||
|
||||
# paginate & response
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None: # noqa
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, request, *args, **kwargs):
|
||||
""" create transaction with product items """
|
||||
|
||||
organization = self.get_device_organization()
|
||||
device = self.get_pos_device()
|
||||
|
||||
serializer = self.serializer_class(data=request.data, context={
|
||||
'organization': organization,
|
||||
'pos_device': device,
|
||||
'request': self.request
|
||||
|
||||
})
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
serializer.save()
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
|
||||
class QuotaPreSaleItemViewSet(viewsets.ModelViewSet, DynamicSearchMixin, POSDeviceMixin):
|
||||
queryset = warehouse_models.QuotaPreSaleItem.objects.all().select_related(
|
||||
'organization', 'transaction', 'sale_item'
|
||||
)
|
||||
serializer_class = warehouse_serializers.QuotaPreSaleItemSerializer
|
||||
322
apps/warehouse/pos/api/v2/serializers.py
Normal file
322
apps/warehouse/pos/api/v2/serializers.py
Normal file
@@ -0,0 +1,322 @@
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.db.transaction import atomic
|
||||
from rest_framework import serializers, status
|
||||
|
||||
from apps.herd.models import Rancher
|
||||
from apps.herd.pos.api.v1.serializers import RancherSerializer
|
||||
from apps.herd.services.services import get_rancher_statistics, rancher_quota_weight
|
||||
from apps.pos_device.models import POSFreeProducts
|
||||
from apps.pos_device.pos.api.v1.serializers.device import DeviceSerializer
|
||||
from apps.pos_device.services.services import pos_organizations_sharing_information
|
||||
from apps.product.exceptions import QuotaSaleTimeException
|
||||
from apps.product.models import (
|
||||
QuotaDistribution,
|
||||
Product, OrganizationQuotaStats
|
||||
)
|
||||
from apps.product.services.services import (
|
||||
quota_live_stock_allocation_info,
|
||||
quota_incentive_plans_info,
|
||||
quota_attribute_value
|
||||
)
|
||||
from apps.warehouse import models as warehouse_models
|
||||
from apps.warehouse.exceptions import WareHouseException
|
||||
from apps.warehouse.services.quota_usage_services import QuotaUsageService
|
||||
|
||||
|
||||
class InventoryEntrySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = warehouse_models.InventoryEntry
|
||||
fields = [
|
||||
"id",
|
||||
"entry_identity",
|
||||
"create_date",
|
||||
"modify_date",
|
||||
"organization",
|
||||
"distribution",
|
||||
"weight",
|
||||
"balance",
|
||||
"lading_number",
|
||||
"delivery_address",
|
||||
"is_confirmed",
|
||||
"notes",
|
||||
]
|
||||
|
||||
def to_representation(self, instance):
|
||||
""" custom output of inventory entry serializer """
|
||||
|
||||
representation = super().to_representation(instance)
|
||||
if instance.document:
|
||||
representation['document'] = instance.document
|
||||
if instance.distribution:
|
||||
# distribution data
|
||||
representation['distribution'] = {
|
||||
'distribution_identity': instance.distribution.distribution_id,
|
||||
'sale_unit': instance.distribution.quota.sale_unit.unit,
|
||||
'id': instance.distribution.id
|
||||
}
|
||||
|
||||
representation['quota'] = {
|
||||
'quota_identity': instance.distribution.quota.quota_id,
|
||||
'quota_weight': instance.distribution.quota.quota_weight,
|
||||
'quota_livestock_allocations': quota_live_stock_allocation_info(
|
||||
instance.distribution.quota
|
||||
),
|
||||
'quota_incentive_plans': quota_incentive_plans_info(instance.distribution.quota)
|
||||
}
|
||||
|
||||
representation['product'] = {
|
||||
'image': instance.distribution.quota.product.img,
|
||||
'name': instance.distribution.quota.product.name,
|
||||
'id': instance.distribution.quota.product.id,
|
||||
}
|
||||
|
||||
representation['pricing'] = {
|
||||
'pricing_attributes': quota_attribute_value(instance.distribution.quota),
|
||||
'sharing': pos_organizations_sharing_information(
|
||||
self.context['device'],
|
||||
instance.distribution.quota,
|
||||
instance.distribution
|
||||
),
|
||||
'base_prices': [
|
||||
{
|
||||
"text": "درب کارخانه", # noqa
|
||||
"name": "base_price_factory",
|
||||
"value": instance.distribution.quota.base_price_factory
|
||||
},
|
||||
{
|
||||
"text": "درب اتحادیه", # noqa
|
||||
"name": "base_price_cooperative",
|
||||
"value": instance.distribution.quota.base_price_cooperative
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if 'rancher' in self.context.keys():
|
||||
# rancher herd & live stock statistics
|
||||
representation['rancher_statistics'] = get_rancher_statistics(self.context['rancher'])
|
||||
|
||||
# rancher live stock statistics by inventory entry
|
||||
representation['rancher_quota_weight_statistics'] = rancher_quota_weight(
|
||||
self.context['rancher'], instance
|
||||
)
|
||||
|
||||
return representation
|
||||
|
||||
|
||||
class InventoryQuotaSaleTransactionSerializer(serializers.ModelSerializer):
|
||||
rancher_national_code = serializers.CharField(max_length=50, required=False)
|
||||
|
||||
class Meta: # noqa
|
||||
model = warehouse_models.InventoryQuotaSaleTransaction
|
||||
fields = '__all__'
|
||||
depth = 0
|
||||
|
||||
def create(self, validated_data):
|
||||
items_data = self.context['request'].data['items']
|
||||
rancher_code = validated_data.pop(
|
||||
'rancher_national_code'
|
||||
) if 'rancher_national_code' in self.context['request'].data.keys() else None
|
||||
|
||||
with atomic():
|
||||
# get rancher with national code
|
||||
rancher = None # noqa
|
||||
if rancher_code:
|
||||
rancher = Rancher.objects.get(national_code=rancher_code)
|
||||
validated_data['rancher'] = rancher
|
||||
|
||||
# if transaction exists, update transaction status
|
||||
transaction = self.Meta.model.objects.filter(
|
||||
transaction_id=validated_data.get('transaction_id')
|
||||
)
|
||||
if transaction.exists():
|
||||
transaction = transaction.first()
|
||||
|
||||
# --- Case 1: success => only update items
|
||||
if transaction.transaction_status == 'success':
|
||||
for item_data in items_data:
|
||||
qs = warehouse_models.InventoryQuotaSaleItem.objects.filter(
|
||||
Q(transaction=transaction) & (
|
||||
Q(free_product_id=item_data.get('free_product', None)) |
|
||||
Q(gov_product_id=item_data.get('gov_product', None))
|
||||
)
|
||||
).update(**item_data)
|
||||
return transaction
|
||||
|
||||
# --- Case 2: not success => update transaction fields + items
|
||||
for field in [
|
||||
'transaction_status',
|
||||
'transaction_status_code',
|
||||
'result_text',
|
||||
'ref_num',
|
||||
'terminal',
|
||||
'payer_cart',
|
||||
'pos_date',
|
||||
'transaction_date',
|
||||
]:
|
||||
if field in validated_data:
|
||||
setattr(transaction, field, validated_data[field])
|
||||
|
||||
transaction.save(update_fields=[
|
||||
'transaction_status',
|
||||
'transaction_status_code',
|
||||
'result_text',
|
||||
'ref_num',
|
||||
'terminal',
|
||||
'payer_cart',
|
||||
'pos_date',
|
||||
'transaction_date',
|
||||
])
|
||||
|
||||
# items can change
|
||||
for item_data in items_data:
|
||||
items = warehouse_models.InventoryQuotaSaleItem.objects.filter(
|
||||
Q(transaction=transaction) & (
|
||||
Q(free_product_id=item_data.get('free_product', None)) |
|
||||
Q(gov_product_id=item_data.get('gov_product', None))
|
||||
)
|
||||
)
|
||||
items.update(**item_data)
|
||||
|
||||
# if transaction status updated as success, call signal for inventory management
|
||||
if validated_data['transaction_status'] == 'success':
|
||||
for sale_item in items:
|
||||
sale_item.inventory_calculation = True
|
||||
sale_item.save()
|
||||
|
||||
return transaction
|
||||
|
||||
# --- Case 3: create new transaction
|
||||
transaction = warehouse_models.InventoryQuotaSaleTransaction.objects.create(
|
||||
seller_organization=self.context['organization'],
|
||||
pos_device=self.context['pos_device'],
|
||||
**validated_data
|
||||
)
|
||||
|
||||
# calculate total price of product items in shopping cart
|
||||
total_price = 0
|
||||
for item_data in items_data:
|
||||
# get product by type
|
||||
gov_product = item_data.pop('gov_product', None)
|
||||
free_product = item_data.pop('free_product', None)
|
||||
|
||||
distribution = QuotaDistribution.objects.filter(
|
||||
id=item_data.pop('quota_distribution')
|
||||
).first() if 'quota_distribution' in item_data.keys() else None
|
||||
|
||||
quota_stat = OrganizationQuotaStats.objects.get(id=item_data.pop('quota_stat'))
|
||||
|
||||
# create item for transaction
|
||||
item = warehouse_models.InventoryQuotaSaleItem.objects.create(
|
||||
transaction=transaction,
|
||||
quota_distribution=distribution,
|
||||
quota_stat=quota_stat,
|
||||
gov_product=Product.objects.get(
|
||||
id=gov_product
|
||||
) if Product.objects.filter(id=gov_product).exists() else None,
|
||||
free_product=POSFreeProducts.objects.get(
|
||||
id=free_product
|
||||
) if POSFreeProducts.objects.filter(id=free_product).exists() else None,
|
||||
**item_data
|
||||
)
|
||||
total_price += item.total_price
|
||||
|
||||
# IF WE DO NOT HAVE DISTRIBUTION, THEN IT IS A FREE PRODUCT TRANSACTION
|
||||
if 'quota_distribution' and 'quota_stat' in item_data.keys():
|
||||
# # create extra sale for distribution
|
||||
# create_extra_sale(transaction=transaction, sale_item=item)
|
||||
#
|
||||
# # create pre sale for distribution
|
||||
# create_pre_sale(transaction=transaction, sale_item=item)
|
||||
|
||||
# calculate quota usage of rancher
|
||||
QuotaUsageService.allocate_usage(
|
||||
rancher=rancher,
|
||||
distribution=distribution,
|
||||
quota_stat=quota_stat,
|
||||
item_data=item_data
|
||||
)
|
||||
|
||||
transaction.transaction_price = total_price
|
||||
transaction.save()
|
||||
|
||||
return transaction
|
||||
|
||||
def validate(self, attrs):
|
||||
"""
|
||||
validate total inventory sale should be fewer than
|
||||
distribution weight
|
||||
"""
|
||||
|
||||
items = self.context['request'].data['items']
|
||||
for item in items:
|
||||
if 'quota_stat' in item.keys():
|
||||
|
||||
# get quota stat object
|
||||
quota_stat = OrganizationQuotaStats.objects.get(
|
||||
id=item.get('quota_stat')
|
||||
)
|
||||
if not quota_stat.quota.pre_sale and not quota_stat.quota.free_sale:
|
||||
# if quota has not been in sale time
|
||||
if not quota_stat.quota.is_in_sale_licence_time():
|
||||
raise QuotaSaleTimeException()
|
||||
total_sale_weight = quota_stat.sale_items.aggregate(
|
||||
total=models.Sum('weight')
|
||||
)['total'] or 0
|
||||
|
||||
if total_sale_weight + item.get('weight') > quota_stat.remaining_amount:
|
||||
raise WareHouseException(
|
||||
"وزن وارد شده بیشتر از باقیمانده سهمیه میباشد", # noqa
|
||||
status_code=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
|
||||
return attrs
|
||||
|
||||
def to_representation(self, instance):
|
||||
""" customize output of transactions serializer """
|
||||
|
||||
representation = super().to_representation(instance)
|
||||
|
||||
if instance.rancher:
|
||||
representation['rancher'] = RancherSerializer(instance.rancher).data
|
||||
representation['pos_device'] = DeviceSerializer(instance.pos_device).data
|
||||
if instance.seller_organization:
|
||||
representation['seller_organization'] = instance.seller_organization.name
|
||||
|
||||
# get product items of transaction
|
||||
representation['items'] = InventoryQuotaSaleItemSerializer(instance.items.all(), many=True).data
|
||||
|
||||
return representation
|
||||
|
||||
|
||||
class InventoryQuotaSaleItemSerializer(serializers.ModelSerializer):
|
||||
product_name = serializers.CharField(source='product.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = warehouse_models.InventoryQuotaSaleItem
|
||||
fields = [
|
||||
'id',
|
||||
"transaction",
|
||||
"quota_stat",
|
||||
"gov_product",
|
||||
"free_product",
|
||||
"product_name",
|
||||
"image",
|
||||
"name",
|
||||
"price_type",
|
||||
"delivery_type",
|
||||
"item_type",
|
||||
"paid_type",
|
||||
"weight",
|
||||
"unit_price",
|
||||
"total_price",
|
||||
"paid_price",
|
||||
"livestock_statistic",
|
||||
"item_share",
|
||||
]
|
||||
|
||||
|
||||
class QuotaPreSaleItemSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = warehouse_models.QuotaPreSaleItem
|
||||
fields = '__all__'
|
||||
14
apps/warehouse/pos/api/v2/urls.py
Normal file
14
apps/warehouse/pos/api/v2/urls.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from . import api
|
||||
|
||||
router = DefaultRouter()
|
||||
|
||||
router.register(r'inventory_entry', api.InventoryEntryViewSet, basename='inventory_entry')
|
||||
router.register(r'transaction', api.InventoryQuotaSaleTransactionViewSet, basename='transaction')
|
||||
router.register(r'pre_sale', api.QuotaPreSaleItemViewSet, basename='pre_sale')
|
||||
|
||||
urlpatterns = [
|
||||
path('v2/', include(router.urls))
|
||||
]
|
||||
@@ -1,15 +1,15 @@
|
||||
from apps.product.models import QuotaUsage, IncentivePlan
|
||||
from apps.livestock.models import LiveStockType
|
||||
from apps.product.models import QuotaUsage, IncentivePlan
|
||||
|
||||
|
||||
class QuotaUsageService:
|
||||
|
||||
@staticmethod
|
||||
def allocate_usage(rancher, distribution, item_data):
|
||||
def allocate_usage(rancher, distribution, item_data, quota_stat=None):
|
||||
""" save & calculate quota usage of rancher """
|
||||
|
||||
# purchase quota usage of rancher
|
||||
if 'livestock_statistic' in item_data.keys():
|
||||
if 'livestock_statistic' in item_data.keys(): # noqa
|
||||
|
||||
# get list of livestock types object for transaction item
|
||||
livestock_types = {
|
||||
@@ -40,6 +40,7 @@ class QuotaUsageService:
|
||||
rancher=rancher,
|
||||
livestock_type=livestock_type,
|
||||
distribution=distribution,
|
||||
quota_stat=quota_stat,
|
||||
incentive_plan=incentive_plans.get(item['id']) if is_incentive else None,
|
||||
defaults={
|
||||
"count": item['count'],
|
||||
|
||||
@@ -24,32 +24,34 @@ 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:
|
||||
# if quota_distribution.warehouse_balance >= 0:
|
||||
#
|
||||
# # calculate extra sales & mines total extra sales weight from new inventory entry
|
||||
# # and set the warehouse balance
|
||||
# extra_sales = quota_distribution.extra_sales.all()
|
||||
# total_extra_sales_weight = extra_sales.aggregate(total=Sum('weight'))['total'] or 0
|
||||
# if quota_distribution.free_sale_balance != 0:
|
||||
# if quota_distribution.warehouse_balance >= quota_distribution.free_sale_balance:
|
||||
# quota_distribution.warehouse_balance -= total_extra_sales_weight
|
||||
# quota_distribution.free_sale_balance = 0
|
||||
# else:
|
||||
# quota_distribution.free_sale_balance -= quota_distribution.warehouse_balance
|
||||
# quota_distribution.warehouse_balance = 0
|
||||
#
|
||||
# # calculate pre_sales & mines total pre_sales weight from new inventory entry
|
||||
# # and set the warehouse balance
|
||||
# pre_sales = quota_distribution.pre_sales.all()
|
||||
# total_pre_sales_weight = pre_sales.aggregate(total=Sum('weight'))['total'] or 0
|
||||
# if total_pre_sales_weight != 0:
|
||||
# if quota_distribution.warehouse_balance >= quota_distribution.pre_sale_balance:
|
||||
# quota_distribution.warehouse_balance -= total_pre_sales_weight
|
||||
# quota_distribution.pre_sale_balance = 0
|
||||
# else:
|
||||
# quota_distribution.pre_sale_balance -= quota_distribution.warehouse_balance
|
||||
# quota_distribution.warehouse_balance = 0
|
||||
|
||||
# calculate extra sales & mines total extra sales weight from new inventory entry
|
||||
# and set the warehouse balance
|
||||
extra_sales = quota_distribution.extra_sales.all()
|
||||
total_extra_sales_weight = extra_sales.aggregate(total=Sum('weight'))['total'] or 0
|
||||
if quota_distribution.free_sale_balance != 0:
|
||||
if quota_distribution.warehouse_balance >= quota_distribution.free_sale_balance:
|
||||
quota_distribution.warehouse_balance -= total_extra_sales_weight
|
||||
quota_distribution.free_sale_balance = 0
|
||||
else:
|
||||
quota_distribution.free_sale_balance -= quota_distribution.warehouse_balance
|
||||
quota_distribution.warehouse_balance = 0
|
||||
|
||||
# calculate pre_sales & mines total pre_sales weight from new inventory entry
|
||||
# and set the warehouse balance
|
||||
pre_sales = quota_distribution.pre_sales.all()
|
||||
total_pre_sales_weight = pre_sales.aggregate(total=Sum('weight'))['total'] or 0
|
||||
if total_pre_sales_weight != 0:
|
||||
if quota_distribution.warehouse_balance >= quota_distribution.pre_sale_balance:
|
||||
quota_distribution.warehouse_balance -= total_pre_sales_weight
|
||||
quota_distribution.pre_sale_balance = 0
|
||||
else:
|
||||
quota_distribution.pre_sale_balance -= quota_distribution.warehouse_balance
|
||||
quota_distribution.warehouse_balance = 0
|
||||
quota_distribution.save(update_fields=['been_sold', 'warehouse_balance', 'free_sale_balance', 'pre_sale_balance'])
|
||||
# 'free_sale_balance', 'pre_sale_balance'
|
||||
quota_distribution.save(update_fields=['been_sold', 'warehouse_balance'])
|
||||
|
||||
|
||||
@receiver(post_init, sender=InventoryEntry)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Your urls go here
|
||||
from django.urls import path, include
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('web/api/', include('apps.warehouse.web.api.v1.urls')),
|
||||
path('pos/api/', include('apps.warehouse.pos.api.v1.urls')),
|
||||
path('pos/api/', include('apps.warehouse.pos.api.v2.urls')),
|
||||
path('excel/', include('apps.warehouse.services.excel.urls')),
|
||||
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user