quota, distribution, inventory entry, quota sale transaction, product informations, signals ,....

This commit is contained in:
2025-07-02 15:42:51 +03:30
parent 2f23c5104d
commit 279afba977
45 changed files with 1408 additions and 88 deletions

View File

@@ -1,6 +1,7 @@
import datetime
from apps.product.web.api.v1 import product_serializers as product_serializers
from apps.product.exceptions import QuotaExpiredTimeException
from rest_framework.exceptions import APIException
from apps.product import models as product_models
from rest_framework.response import Response
@@ -10,6 +11,7 @@ from rest_framework import viewsets
from rest_framework import status
from django.db import transaction
from django.db.models import Q
from datetime import datetime
def trash(queryset, pk): # noqa
@@ -65,6 +67,10 @@ class ProductViewSet(viewsets.ModelViewSet):
queryset = product_models.Product.objects.all()
serializer_class = product_serializers.ProductSerializer
def list(self, request, *args, **kwargs):
product = self.queryset.get(id=1)
return Response(product.quota_information(), status.HTTP_200_OK)
@action(
methods=['put'],
detail=True,
@@ -346,24 +352,26 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
user_relation = request.user.user_relation.all().first()
# add user relation to data
request.data['registerer_organization'] = user_relation.id
request.data['registerer_organization'] = user_relation.organization.id
# create quota
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
quota = serializer.save()
quota.remaining_quota_weight = quota.quota_weight
quota.remaining_weight = quota.quota_weight
# create incentive plan
plans_list = []
if 'incentive_plan_data' in request.data.keys():
incentive_plan = CustomOperations().custom_create(
request=request,
view=QuotaIncentiveAssignmentViewSet(),
data_key='incentive_plan_data',
additional_data={'quota': quota.id}
)
else:
incentive_plan = {}
for plan in request.data['incentive_plan_data']:
plan.update({'quota': quota.id})
incentive_plan = CustomOperations().custom_create(
request=request,
view=QuotaIncentiveAssignmentViewSet(),
data_key='incentive_plan_data',
data=plan
)
plans_list.append(incentive_plan)
# create product price attributes for quota
attributes_value_list = []
@@ -376,7 +384,6 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
data=attr
)
attributes_value_list.append(attributes)
# create product broker values for quota
broker_data_list = []
if 'broker_data' in request.data.keys():
@@ -400,7 +407,6 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
data=ls_alloc
)
allocations_list.append(allocations)
# create livestock age limits for quota
livestock_age_limits = []
if 'livestock_age_limitations' in request.data.keys():
@@ -415,19 +421,133 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
data = {
'quota': serializer.data,
'incentive_plan': incentive_plan,
'incentive_plan': plans_list, # noqa
'attribute_values': attributes_value_list,
'broker_values': broker_data_list,
'live_stock_allocations': allocations_list,
'livestock_age_limitations': livestock_age_limits
}
# call save method to generate id & calculate quota final price
quota.save(calculate_final_price=True)
return Response(data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)
@transaction.atomic
def update(self, request, *args, **kwargs):
pass
def update(self, request, pk=None, *args, **kwargs):
# get user relations data like organization
user_relation = request.user.user_relation.all().first()
# add user relation to data
request.data['registerer_organization'] = user_relation.organization.id
# create quota
serializer = self.serializer_class(data=request.data, instance=self.get_object(), partial=True)
if serializer.is_valid():
quota = serializer.save()
# create incentive plan
plans_list = []
if 'incentive_plan_data' in request.data.keys():
for plan in request.data['incentive_plan_data']:
plan.update({'quota': quota.id})
incentive_plan = CustomOperations().custom_update(
request=request,
view=QuotaIncentiveAssignmentViewSet(),
data_key='incentive_plan_data',
obj_id=plan['id'],
data=plan
)
plans_list.append(incentive_plan)
# create product price attributes for quota
attributes_value_list = [] # noqa
if 'price_attributes_data' in request.data.keys():
for attr in request.data['price_attributes_data']:
attr.update({'quota': quota.id})
attributes = CustomOperations().custom_update(
request=request,
view=AttributeValueViewSet(),
obj_id=attr['id'],
data=attr
)
attributes_value_list.append(attributes)
# create product broker values for quota
broker_data_list = []
if 'broker_data' in request.data.keys():
for broker in request.data['broker_data']:
broker.update({'quota': quota.id})
broker_value = CustomOperations().custom_update(
request=request,
view=QuotaBrokerValueViewSet(),
obj_id=broker['id'],
data=broker
)
broker_data_list.append(broker_value)
# create livestock allocations to quota
allocations_list = []
if 'livestock_allocation_data' in request.data.keys():
for ls_alloc in request.data['livestock_allocation_data']:
ls_alloc.update({'quota': quota.id})
allocations = CustomOperations().custom_update(
request=request,
view=QuotaLiveStockAllocationViewSet(),
obj_id=ls_alloc['id'],
data=ls_alloc
)
allocations_list.append(allocations)
# create livestock age limits for quota
livestock_age_limits = []
if 'livestock_age_limitations' in request.data.keys():
for age_limit in request.data['livestock_age_limitations']:
age_limit.update({'quota': quota.id})
age_limit_creation_object = CustomOperations().custom_update(
request=request,
view=QuotaLiveStockAgeLimitation(),
obj_id=age_limit['id'],
data=age_limit
)
livestock_age_limits.append(age_limit_creation_object)
data = {
'quota': serializer.data,
'incentive_plan': plans_list, # noqa
'attribute_values': attributes_value_list,
'broker_values': broker_data_list,
'live_stock_allocations': allocations_list,
'livestock_age_limitations': livestock_age_limits
}
# call save method to generate id & calculate quota final price
quota.save(calculate_final_price=True)
return Response(data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)
@action(
methods=['patch'],
detail=True,
url_path='close',
url_name='close',
name='close'
)
@transaction.atomic
def close(self, request, pk=None):
""" to close quota """
quota = self.get_object()
# check quota expired time
if not quota.is_in_valid_time():
raise QuotaExpiredTimeException()
if quota.is_closed:
raise APIException("این سهمیه قبلا بسته شده است", status.HTTP_400_BAD_REQUEST) # noqa
quota.is_closed = True
quota.closed_at = datetime.now()
quota.save()
return Response(status.HTTP_200_OK)
@action(
methods=['get'],
@@ -440,7 +560,7 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
""" list of quotas for creator """
assigner = product_models.UserRelations.objects.filter(user=request.user).first()
serializers = self.serializer_class(
self.queryset.filter(registerer_organization=assigner),
self.queryset.filter(registerer_organization=assigner.organization),
many=True
).data
return Response(serializers.data, status=status.HTTP_200_OK)
@@ -456,7 +576,7 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
""" list of quotas for assigned organizations """
assigned = product_models.UserRelations.objects.filter(user=request.user).first()
serializer = self.serializer_class(
self.queryset.filter(assigned_organizations=assigned),
self.queryset.filter(assigned_organizations=assigned.organization),
many=True
)

View File

@@ -1,6 +1,7 @@
from rest_framework import serializers
from apps.product import models as product_models
from apps.authorization.api.v1 import serializers as authorize_serializers
from apps.authentication.api.v1.serializers.serializer import OrganizationSerializer
class ProductCategorySerializer(serializers.ModelSerializer):
@@ -53,16 +54,14 @@ class AttributeValueSerializer(serializers.ModelSerializer):
model = product_models.AttributeValue
fields = '__all__'
def to_representation(self, instance):
""" Custom output """
def update(self, instance, validated_data):
representation = super().to_representation(instance)
if instance.quota:
representation['quota'] = QuotaSerializer(instance.quota).data
if instance.attribute:
representation['attribute'] = AttributeSerializer(instance.attribute).data
instance.quota = validated_data.get('quota', instance.quota)
instance.attribute = validated_data.get('attribute', instance.attribute)
instance.value = validated_data.get('value', instance.value)
instance.save()
return representation
return instance
class BrokerSerializer(serializers.ModelSerializer):
@@ -74,9 +73,9 @@ class BrokerSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.organization_relations:
representation['organization_relations'] = authorize_serializers.UserRelationSerializer(
instance.organization_relations
if instance.organization:
representation['organization'] = OrganizationSerializer(
instance.organization
).data
return representation
@@ -109,6 +108,66 @@ class QuotaSerializer(serializers.ModelSerializer):
class Meta:
model = product_models.Quota
fields = '__all__'
depth = 0
def to_representation(self, instance):
representation = super().to_representation(instance)
if isinstance(instance, product_models.Quota):
representation['incentive_plan'] = QuotaIncentiveAssignmentSerializer(
instance.incentive_assignments.all(),
many=True
).data
representation['attribute_values'] = AttributeValueSerializer(
instance.attribute_values.all(),
many=True
).data
representation['brokers'] = QuotaBrokerValueSerializer(
instance.broker_values.all(),
many=True
).data
representation['livestock_allocations'] = QuotaLiveStockAllocationSerializer(
instance.livestock_allocations.all(),
many=True
).data
representation['livestock_limitations'] = QuotaLiveStockAgeLimitationSerializer(
instance.livestock_age_limitations.all(),
many=True
).data
return representation
def update(self, instance, validated_data):
""" Custom Update """
instance.quota_id = validated_data.get('quota_id', instance.quota_id)
instance.quota_code = validated_data.get('quota_code', instance.quota_code)
instance.quota_weight = validated_data.get('quota_weight', instance.quota_weight)
instance.remaining_weight = validated_data.get('remaining_weight', instance.remaining_weight)
instance.quota_distributed = validated_data.get('quota_distributed', instance.quota_distributed)
instance.quota_balance = validated_data.get('quota_balance', instance.quota_balance)
instance.product = validated_data.get('product', instance.product)
instance.sale_type = validated_data.get('sale_type', instance.sale_type)
instance.month_choices = validated_data.get('month_choices', instance.month_choices)
instance.group = validated_data.get('group', instance.group)
instance.has_distribution_limit = validated_data.get('has_distribution_limit', instance.has_distribution_limit)
instance.distribution_mode = validated_data.get('distribution_mode', instance.distribution_mode)
instance.base_price_factory = validated_data.get('base_price_factory', instance.base_price_factory)
instance.base_price_cooperative = validated_data.get('base_price_cooperative', instance.base_price_cooperative)
instance.final_price = validated_data.get('final_price', instance.final_price)
instance.is_closed = validated_data.get('is_closed', instance.is_closed)
instance.closed_at = validated_data.get('closed_at', instance.closed_at)
instance.save()
instance.assigned_organizations.clear()
instance.assigned_organizations.add(
*(validated_data.get('assigned_organizations', instance.assigned_organizations))
)
return instance
class QuotaIncentiveAssignmentSerializer(serializers.ModelSerializer):
@@ -116,20 +175,74 @@ class QuotaIncentiveAssignmentSerializer(serializers.ModelSerializer):
model = product_models.QuotaIncentiveAssignment
fields = '__all__'
def update(self, instance, validated_data):
""" Custom Update """
instance.quota = validated_data.get('quota', instance.quota)
instance.incentive_plan = validated_data.get('incentive_plan', instance.incentive_plan)
instance.heavy_value = validated_data.get('heavy_value', instance.heavy_value)
instance.light_value = validated_data.get('light_value', instance.light_value)
instance.save()
return instance
class QuotaBrokerValueSerializer(serializers.ModelSerializer): # noqa
class Meta:
model = product_models.QuotaBrokerValue
fields = '__all__'
def update(self, instance, validated_data):
""" Custom Update """
instance.quota = validated_data.get('quota', instance.quota)
instance.broker = validated_data.get('broker', instance.broker)
instance.value = validated_data.get('value', instance.value)
instance.save()
return instance
class QuotaLiveStockAllocationSerializer(serializers.ModelSerializer):
class Meta:
model = product_models.QuotaLivestockAllocation
fields = '__all__'
extra_kwargs = {
'livestock_group': {
'required': False
},
'livestock_type': {
'required': False
},
'livestock_subtype': {
'required': False
}
}
def update(self, instance, validated_data):
""" Custom Update """
instance.quota = validated_data.get('quota', instance.quota)
instance.livestock_group = validated_data.get('livestock_group', instance.livestock_group)
instance.livestock_type = validated_data.get('livestock_type', instance.livestock_type)
instance.livestock_subtype = validated_data.get('livestock_subtype', instance.livestock_subtype)
instance.save()
return instance
class QuotaLiveStockAgeLimitationSerializer(serializers.ModelSerializer):
class Meta:
model = product_models.QuotaLiveStockAgeLimitation
fields = '__all__'
def update(self, instance, validated_data):
""" Custom Update """
instance.quota = validated_data.get('quota', instance.quota)
instance.livestock_type = validated_data.get('livestock_type', instance.livestock_type)
instance.livestock_subtype = validated_data.get('livestock_subtype', instance.livestock_subtype)
instance.age_month = validated_data.get('age_month', instance.age_month)
instance.save()
return instance

View File

@@ -42,7 +42,7 @@ class QuotaDistributionViewSet(viewsets.ModelViewSet):
except APIException as e:
raise APIException("unauthorized", code=status.HTTP_401_UNAUTHORIZED)
request.data.update({'assigner_organization': assigner_user.id})
request.data.update({'assigner_organization': assigner_user.organization.id})
serializer = self.serializer_class(data=request.data)
if serializer.is_valid(raise_exception=True):
@@ -77,6 +77,14 @@ class QuotaDistributionViewSet(viewsets.ModelViewSet):
@transaction.atomic
def trash(self, request, pk=None):
""" Sent quota distribution to trash """
quota_distribution = self.get_object()
# check if distribution has inventory entry
if quota_distribution.inventory_entry.exists():
raise APIException(
"امکان حذف این توزیع وجود ندارد. ورود به انبار برای آن ثبت شده است", # noqa
status.HTTP_400_BAD_REQUEST
)
try:
trash(self.queryset, pk)
except APIException as e:
@@ -92,6 +100,14 @@ class QuotaDistributionViewSet(viewsets.ModelViewSet):
@transaction.atomic
def delete(self, request, pk=None):
""" Full delete of quota distribution object """
quota_distribution = self.get_object()
# check if distribution has inventory entry
if quota_distribution.inventory_entry.exists():
raise APIException(
"امکان حذف این توزیع وجود ندارد. ورود به انبار برای آن ثبت شده است", # noqa
status.HTTP_400_BAD_REQUEST
)
try:
delete(self.queryset, pk)
return Response(status=status.HTTP_200_OK)

View File

@@ -2,7 +2,11 @@ from rest_framework import serializers
from apps.product import models as product_models
from apps.product.web.api.v1.product_serializers import QuotaSerializer
from django.db import models
from apps.product.exceptions import QuotaWeightException
from apps.product.exceptions import (
QuotaWeightException,
QuotaClosedException,
QuotaExpiredTimeException
)
from rest_framework import status
@@ -17,13 +21,25 @@ class QuotaDistributionSerializer(serializers.ModelSerializer):
}
def validate(self, data):
""" to validate if distribution weight
more than quota weight raise exception """
"""
to validate if distribution weight
more than quota weight raise exception
or if quota is closed raise exception
"""
quota = data['quota']
amount = data['weight']
instance_id = self.instance.id if self.instance else None
# check quota expired time
if not quota.is_in_valid_time():
raise QuotaExpiredTimeException()
# check if quota is closed
if quota.is_closed:
raise QuotaClosedException()
# total quota distributions weight
total = product_models.QuotaDistribution.objects.filter(
quota=quota
).exclude(id=instance_id).aggregate(