feat: 采购计划和订单增加单价和总价
This commit is contained in:
parent
9e26f416a5
commit
3f62e8709f
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 3.2.12 on 2024-07-31 10:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pum', '0007_supplier_number'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puorder',
|
||||||
|
name='total_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='总价'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puorderitem',
|
||||||
|
name='total_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='总价'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puorderitem',
|
||||||
|
name='unit_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='单价'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puplan',
|
||||||
|
name='total_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='总价'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puplanitem',
|
||||||
|
name='total_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='总价'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='puplanitem',
|
||||||
|
name='unit_price',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True, verbose_name='单价'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -39,6 +39,7 @@ class PuPlan(CommonBModel):
|
||||||
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
||||||
submit_user = models.ForeignKey(
|
submit_user = models.ForeignKey(
|
||||||
'system.user', verbose_name='提交人', related_name='submit_user_puplan', on_delete=models.CASCADE, null=True, blank=True)
|
'system.user', verbose_name='提交人', related_name='submit_user_puplan', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class PuOrder(CommonBModel):
|
class PuOrder(CommonBModel):
|
||||||
|
@ -66,6 +67,7 @@ class PuOrder(CommonBModel):
|
||||||
'system.user', verbose_name='提交人', related_name='submit_user_puorder', on_delete=models.CASCADE, null=True, blank=True)
|
'system.user', verbose_name='提交人', related_name='submit_user_puorder', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
materials = models.ManyToManyField(
|
materials = models.ManyToManyField(
|
||||||
Material, verbose_name='多个物料', blank=True, through='pum.puorderitem', related_name='pu_order_materials')
|
Material, verbose_name='多个物料', blank=True, through='pum.puorderitem', related_name='pu_order_materials')
|
||||||
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class PuOrderItem(BaseModel):
|
class PuOrderItem(BaseModel):
|
||||||
|
@ -73,6 +75,8 @@ class PuOrderItem(BaseModel):
|
||||||
Material, verbose_name='物料', on_delete=models.CASCADE, related_name='pu_orderitem_material')
|
Material, verbose_name='物料', on_delete=models.CASCADE, related_name='pu_orderitem_material')
|
||||||
count = models.PositiveIntegerField('所需数量', default=0)
|
count = models.PositiveIntegerField('所需数量', default=0)
|
||||||
delivered_count = models.PositiveIntegerField('已到货数量', default=0)
|
delivered_count = models.PositiveIntegerField('已到货数量', default=0)
|
||||||
|
unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
||||||
on_delete=models.CASCADE, null=True, blank=True, related_name='item_puorder')
|
on_delete=models.CASCADE, null=True, blank=True, related_name='item_puorder')
|
||||||
|
|
||||||
|
@ -85,6 +89,8 @@ class PuPlanItem(CommonBDModel):
|
||||||
Material, verbose_name='所需物料', on_delete=models.CASCADE)
|
Material, verbose_name='所需物料', on_delete=models.CASCADE)
|
||||||
need_count = models.PositiveIntegerField('所属数量')
|
need_count = models.PositiveIntegerField('所属数量')
|
||||||
need_date = models.DateField('需求日期')
|
need_date = models.DateField('需求日期')
|
||||||
|
unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
||||||
note = models.TextField('备注', default='')
|
note = models.TextField('备注', default='')
|
||||||
pu_plan = models.ForeignKey(PuPlan, verbose_name='采购计划', on_delete=models.CASCADE,
|
pu_plan = models.ForeignKey(PuPlan, verbose_name='采购计划', on_delete=models.CASCADE,
|
||||||
null=True, blank=True, related_name='item_puplan')
|
null=True, blank=True, related_name='item_puplan')
|
||||||
|
|
|
@ -5,6 +5,8 @@ from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem
|
from apps.pum.models import Supplier, PuPlan, PuPlanItem, PuOrder, PuOrderItem
|
||||||
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
|
from apps.mtm.serializers import MaterialSerializer, MaterialSimpleSerializer
|
||||||
|
from django.db import transaction
|
||||||
|
from .services import PumService
|
||||||
|
|
||||||
|
|
||||||
class SupplierSerializer(CustomModelSerializer):
|
class SupplierSerializer(CustomModelSerializer):
|
||||||
|
@ -18,7 +20,7 @@ class PuPlanSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PuPlan
|
model = PuPlan
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS_DEPT + ['state', 'submit_time']
|
read_only_fields = EXCLUDE_FIELDS_DEPT + ['state', 'submit_time', 'total_price']
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
if instance.state != PuPlan.PUPLAN_CREATE:
|
if instance.state != PuPlan.PUPLAN_CREATE:
|
||||||
|
@ -41,6 +43,7 @@ class PuPlanItemSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS + ['pu_order']
|
read_only_fields = EXCLUDE_FIELDS + ['pu_order']
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
pu_plan = validated_data['pu_plan']
|
pu_plan = validated_data['pu_plan']
|
||||||
if pu_plan.state != PuPlan.PUPLAN_CREATE:
|
if pu_plan.state != PuPlan.PUPLAN_CREATE:
|
||||||
|
@ -49,7 +52,9 @@ class PuPlanItemSerializer(CustomModelSerializer):
|
||||||
self.complete_belong_dept(validated_data)
|
self.complete_belong_dept(validated_data)
|
||||||
if PuPlanItem.objects.filter(material=material, belong_dept=validated_data['belong_dept']).exists():
|
if PuPlanItem.objects.filter(material=material, belong_dept=validated_data['belong_dept']).exists():
|
||||||
raise ValidationError('同部门已提交该物料需求,请确认!')
|
raise ValidationError('同部门已提交该物料需求,请确认!')
|
||||||
return super().create(validated_data)
|
ins = super().create(validated_data)
|
||||||
|
PumService.cal_pu_plan_total_price(pu_plan)
|
||||||
|
return ins
|
||||||
|
|
||||||
def complete_belong_dept(self, validated_data):
|
def complete_belong_dept(self, validated_data):
|
||||||
belong_dept = validated_data.get('belong_dept', None)
|
belong_dept = validated_data.get('belong_dept', None)
|
||||||
|
@ -59,7 +64,8 @@ class PuPlanItemSerializer(CustomModelSerializer):
|
||||||
raise ValidationError('所属部门不可为空')
|
raise ValidationError('所属部门不可为空')
|
||||||
else:
|
else:
|
||||||
validated_data['belong_dept'] = belong_dept
|
validated_data['belong_dept'] = belong_dept
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
validated_data.pop('pu_plan')
|
validated_data.pop('pu_plan')
|
||||||
pu_plan = instance.pu_plan
|
pu_plan = instance.pu_plan
|
||||||
|
@ -73,7 +79,9 @@ class PuPlanItemSerializer(CustomModelSerializer):
|
||||||
self.complete_belong_dept(validated_data)
|
self.complete_belong_dept(validated_data)
|
||||||
if PuPlanItem.objects.exclude(id=instance.id).filter(material=material, belong_dept=validated_data['belong_dept']).exists():
|
if PuPlanItem.objects.exclude(id=instance.id).filter(material=material, belong_dept=validated_data['belong_dept']).exists():
|
||||||
raise ValidationError('同部门已提交该物料需求,请确认!')
|
raise ValidationError('同部门已提交该物料需求,请确认!')
|
||||||
return super().update(instance, validated_data)
|
ins = super().update(instance, validated_data)
|
||||||
|
PumService.cal_pu_plan_total_price(pu_plan)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
class PuOrderSerializer(CustomModelSerializer):
|
class PuOrderSerializer(CustomModelSerializer):
|
||||||
|
@ -89,7 +97,7 @@ class PuOrderSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PuOrder
|
model = PuOrder
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS_DEPT + ['state', 'submit_time']
|
read_only_fields = EXCLUDE_FIELDS_DEPT + ['state', 'submit_time', 'total_price']
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
validated_data.pop('supplier')
|
validated_data.pop('supplier')
|
||||||
|
@ -106,6 +114,7 @@ class PuOrderItemSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = EXCLUDE_FIELDS_BASE + ['delivered_count']
|
read_only_fields = EXCLUDE_FIELDS_BASE + ['delivered_count']
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
pu_order = validated_data['pu_order']
|
pu_order = validated_data['pu_order']
|
||||||
material = validated_data['material']
|
material = validated_data['material']
|
||||||
|
@ -113,15 +122,20 @@ class PuOrderItemSerializer(CustomModelSerializer):
|
||||||
raise ValidationError('采购订单该状态下不可添加明细')
|
raise ValidationError('采购订单该状态下不可添加明细')
|
||||||
if PuOrderItem.objects.filter(pu_order=pu_order, material=material).exists():
|
if PuOrderItem.objects.filter(pu_order=pu_order, material=material).exists():
|
||||||
raise ValidationError('该物料已添加')
|
raise ValidationError('该物料已添加')
|
||||||
return super().create(validated_data)
|
ins = super().create(validated_data)
|
||||||
|
PumService.cal_pu_order_total_price(pu_order)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
validated_data.pop('material')
|
validated_data.pop('material')
|
||||||
validated_data.pop('pu_order')
|
validated_data.pop('pu_order')
|
||||||
pu_order = instance.pu_order
|
pu_order = instance.pu_order
|
||||||
if pu_order.state != PuOrder.PUORDER_CREATE:
|
if pu_order.state != PuOrder.PUORDER_CREATE:
|
||||||
raise ValidationError('采购订单该状态下不可编辑')
|
raise ValidationError('采购订单该状态下不可编辑')
|
||||||
return super().update(instance, validated_data)
|
ins = super().update(instance, validated_data)
|
||||||
|
PumService.cal_pu_order_total_price(pu_order)
|
||||||
|
return ins
|
||||||
|
|
||||||
|
|
||||||
class AddSerializer(serializers.Serializer):
|
class AddSerializer(serializers.Serializer):
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from apps.pum.models import PuOrderItem, PuPlan, PuPlanItem, PuOrder
|
from apps.pum.models import PuOrderItem, PuPlan, PuPlanItem, PuOrder
|
||||||
from django.db.models import F
|
from django.db.models import F, Sum
|
||||||
from apps.inm.models import MIO, MIOItem
|
from apps.inm.models import MIO, MIOItem
|
||||||
|
|
||||||
|
|
||||||
class PumService:
|
class PumService:
|
||||||
|
|
||||||
|
def cal_pu_order_total_price(puorder: PuOrder):
|
||||||
|
total_price = PuOrderItem.objects.filter(pu_order=puorder).aggregate(total=Sum("total_price"))["total"] or 0
|
||||||
|
puorder.total_price = total_price
|
||||||
|
puorder.save()
|
||||||
|
|
||||||
|
def cal_pu_plan_total_price(puplan: PuPlan):
|
||||||
|
total_price = PuPlanItem.objects.filter(pu_plan=puplan).aggregate(total=Sum("total_price"))["total"] or 0
|
||||||
|
puplan.total_price = total_price
|
||||||
|
puplan.save()
|
||||||
|
|
||||||
def change_puplan_state_when_puorder_sumbit(puorder: PuOrder):
|
def change_puplan_state_when_puorder_sumbit(puorder: PuOrder):
|
||||||
puplanIds = PuPlanItem.objects.filter(
|
puplanIds = PuPlanItem.objects.filter(
|
||||||
pu_order=puorder).values_list('pu_plan', flat=True)
|
pu_order=puorder).values_list('pu_plan', flat=True)
|
||||||
|
|
|
@ -45,7 +45,7 @@ class PuPlanViewSet(CustomModelViewSet):
|
||||||
def perform_destroy(self, instance):
|
def perform_destroy(self, instance):
|
||||||
if PuPlanItem.objects.filter(pu_plan=instance).exists():
|
if PuPlanItem.objects.filter(pu_plan=instance).exists():
|
||||||
raise ParseError('该计划存在明细不可删除')
|
raise ParseError('该计划存在明细不可删除')
|
||||||
return super().perform_destroy(instance)
|
instance.delete(soft=False)
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post': 'pu_plan.submit'}, serializer_class=serializers.Serializer)
|
@action(methods=['post'], detail=True, perms_map={'post': 'pu_plan.submit'}, serializer_class=serializers.Serializer)
|
||||||
def submit(self, request, *args, **kwargs):
|
def submit(self, request, *args, **kwargs):
|
||||||
|
@ -80,6 +80,7 @@ class PuPlanItemViewSet(CustomModelViewSet):
|
||||||
ordering_fields = ['create_time', 'material', 'need_date', 'need_count']
|
ordering_fields = ['create_time', 'material', 'need_date', 'need_count']
|
||||||
ordering = ['create_time']
|
ordering = ['create_time']
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def perform_destroy(self, instance):
|
def perform_destroy(self, instance):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
pu_plan = instance.pu_plan
|
pu_plan = instance.pu_plan
|
||||||
|
@ -87,7 +88,8 @@ class PuPlanItemViewSet(CustomModelViewSet):
|
||||||
raise ParseError('非创建人不可删除')
|
raise ParseError('非创建人不可删除')
|
||||||
elif instance.pu_order is not None:
|
elif instance.pu_order is not None:
|
||||||
raise ParseError('存在采购订单不可删除')
|
raise ParseError('存在采购订单不可删除')
|
||||||
return super().perform_destroy(instance)
|
instance.delete()
|
||||||
|
PumService.cal_pu_plan_total_price(pu_plan)
|
||||||
|
|
||||||
|
|
||||||
class PuOrderViewSet(CustomModelViewSet):
|
class PuOrderViewSet(CustomModelViewSet):
|
||||||
|
@ -149,6 +151,7 @@ class PuOrderItemViewSet(CustomModelViewSet):
|
||||||
if pu_order.state != PuOrder.PUORDER_CREATE:
|
if pu_order.state != PuOrder.PUORDER_CREATE:
|
||||||
raise ParseError('采购订单非创建中不可删除')
|
raise ParseError('采购订单非创建中不可删除')
|
||||||
instance.delete()
|
instance.delete()
|
||||||
|
PumService.cal_pu_order_total_price(pu_order)
|
||||||
|
|
||||||
@action(methods=['post'], detail=False, perms_map={'post': 'pu_order.update'}, serializer_class=AddSerializer)
|
@action(methods=['post'], detail=False, perms_map={'post': 'pu_order.update'}, serializer_class=AddSerializer)
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -172,10 +175,16 @@ class PuOrderItemViewSet(CustomModelViewSet):
|
||||||
puorderitem, is_created = PuOrderItem.objects.get_or_create(
|
puorderitem, is_created = PuOrderItem.objects.get_or_create(
|
||||||
pu_order=puorder, material=item.material,
|
pu_order=puorder, material=item.material,
|
||||||
defaults={'pu_order': puorder,
|
defaults={'pu_order': puorder,
|
||||||
'material': item.material, 'count': item.need_count}
|
'material': item.material,
|
||||||
|
'count': item.need_count,
|
||||||
|
'unit_price': item.unit_price,
|
||||||
|
'total_price': item.total_price
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if not is_created:
|
if not is_created:
|
||||||
puorderitem.count = puorderitem.count + item.need_count
|
puorderitem.count = puorderitem.count + item.need_count
|
||||||
|
if item.total_price:
|
||||||
|
puorderitem.total_price = puorderitem.total_price if puorderitem.total_price else 0 + item.total_price
|
||||||
puorderitem.save()
|
puorderitem.save()
|
||||||
if puorder.delivery_date is None:
|
if puorder.delivery_date is None:
|
||||||
puorder.delivery_date = item.need_date
|
puorder.delivery_date = item.need_date
|
||||||
|
|
Loading…
Reference in New Issue