factory/apps/inm/serializers.py

491 lines
21 KiB
Python

from rest_framework import serializers
from rest_framework.exceptions import ValidationError, ParseError
from apps.mtm.serializers import MaterialSerializer
from apps.pum.models import PuOrder
from apps.sam.models import Order
from apps.system.models import Dept, User
from apps.utils.constants import EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS_DEPT, EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer
from apps.mtm.models import Material
from .models import MIO, MaterialBatch, MIOItem, WareHouse, MIOItemA, MaterialBatchA, MIOItemw, Pack
from django.db import transaction
from django.db.models import F, Sum, DecimalField
from server.settings import get_sysconfig
from apps.wpmw.models import Wpr
from decimal import Decimal
class WareHourseSerializer(CustomModelSerializer):
class Meta:
model = WareHouse
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS_DEPT
class MaterialBatchAListSerializer(CustomModelSerializer):
material_ = MaterialSerializer(source='material', read_only=True)
class Meta:
model = MaterialBatchA
fields = ['material', 'batch', 'rate', 'mb', 'id', 'material_']
class MaterialBatchAListSerializer2(CustomModelSerializer):
material_name = serializers.StringRelatedField(
source='material', read_only=True)
class Meta:
model = MaterialBatchA
fields = ['material', 'batch', 'rate', 'mb',
'id', 'material_name']
class MaterialBatchSerializer(CustomModelSerializer):
warehouse_name = serializers.CharField(
source='warehouse.name', read_only=True)
material_name = serializers.StringRelatedField(
source='material', read_only=True)
supplier_name = serializers.StringRelatedField(
source='supplier', read_only=True)
material_ = MaterialSerializer(source='material', read_only=True)
defect_name = serializers.CharField(source="defect.name", read_only=True)
count_mioing = serializers.IntegerField(read_only=True, label='正在出入库数量')
class Meta:
model = MaterialBatch
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS_BASE
def to_representation(self, instance):
ret = super().to_representation(instance)
if 'count' in ret and 'count_mioing' in ret:
ret['count_canmio'] = str(Decimal(ret['count']) - Decimal(ret['count_mioing']))
return ret
class MaterialBatchDetailSerializer(CustomModelSerializer):
warehouse_name = serializers.CharField(
source='warehouse.name', read_only=True)
material_name = serializers.CharField(
source='material.name', read_only=True)
material_ = MaterialSerializer(source='material', read_only=True)
assemb = MaterialBatchAListSerializer(
source='a_mb', read_only=True, many=True)
supplier_name = serializers.StringRelatedField(
source='supplier', read_only=True)
class Meta:
model = MaterialBatch
fields = '__all__'
class MIOListSerializer(CustomModelSerializer):
create_by_name = serializers.CharField(
source='create_by.name', read_only=True)
submit_user_name = serializers.CharField(
source='submit_user.name', read_only=True)
do_user_name = serializers.CharField(
source='do_user.name', read_only=True)
mio_user_name = serializers.CharField(
source='mio_user.name', read_only=True)
belong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
supplier_name = serializers.CharField(
source='supplier.name', read_only=True)
customer_name = serializers.CharField(
source='customer.name', read_only=True)
order_number = serializers.CharField(source='order.number', read_only=True)
pu_order_number = serializers.CharField(
source='pu_order.number', read_only=True)
total_price = serializers.SerializerMethodField(label='总价格')
class Meta:
model = MIO
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS + \
['state', 'submit_time', 'submit_user', 'number']
def get_total_price(self, obj):
res = MIOItem.objects.filter(mio=obj, unit_price__isnull=False).aggregate(
total=Sum(F('count') * F('unit_price'),
output_field=DecimalField(max_digits=18, decimal_places=4)))
return res['total']
class MIOItemACreateSerializer(CustomModelSerializer):
class Meta:
model = MIOItemA
fields = ['material', 'batch', 'zhuidu',
'b_zuidawaijing', 'g_zuidaneijing']
class MIOItemwSerializer(CustomModelSerializer):
class Meta:
model = MIOItemw
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ["mioitem"]
class MIOItemCreateSerializer(CustomModelSerializer):
assemb = MIOItemACreateSerializer(
label='组合件信息', many=True, write_only=True, required=False)
mioitemw = MIOItemwSerializer(many=True, required=False)
class Meta:
model = MIOItem
fields = ['mio', 'warehouse', 'material',
'batch', 'count', 'assemb', 'is_testok', 'mioitemw', 'mb', 'wm', 'unit_price', 'note', "pack_index", "count_send"]
extra_kwargs = {
'mio': {'required': True}, 'warehouse': {'required': False},
'material': {'required': False}, 'batch': {'required': False, "allow_null": True, "allow_blank": True}}
def create(self, validated_data):
mio:MIO = validated_data['mio']
mio_type = mio.type
mb = validated_data.get('mb', None)
wm = validated_data.get('wm', None)
assemb = validated_data.pop('assemb', [])
if mio.type == MIO.MIO_TYPE_DO_IN and not assemb:
if not wm:
raise ParseError('生产入库必须指定车间库存')
elif mio.type == MIO.MIO_TYPE_DO_OUT:
if not mb:
raise ParseError('生产领料必须指定仓库库存')
if mb:
validated_data["material"] = mb.material
validated_data["batch"] = mb.batch
validated_data["warehouse"] = mb.warehouse
elif wm:
validated_data["material"] = wm.material
validated_data["batch"] = wm.batch
material: Material = validated_data['material']
batch = validated_data.get("batch", None)
if not batch:
batch = ""
if batch != '' and len(batch) < 5:
raise ParseError('批次号格式错误')
if material.is_hidden:
raise ParseError('隐式物料不可出入库')
if mio.type in [MIO.MIO_TYPE_RETURN_IN, MIO.MIO_TYPE_BORROW_OUT]:
if not material.into_wm:
raise ParseError('该物料不可领用或归还')
if mio.state != MIO.MIO_CREATE:
raise ParseError('出入库记录非创建中不可新增')
# 生产领料要校验是否进行检验
# 某些客户此处无需校验
check_test_when_do_out = get_sysconfig('mes.check_test_when_do_out', True)
if check_test_when_do_out and mio.type == MIO.MIO_TYPE_DO_OUT:
mis = MIOItem.objects.filter(batch=batch, material=material, mio__type__in=[MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_OTHER_IN])
if mis.exists() and (not mis.exclude(test_date=None).exists()):
raise ParseError('该批次的物料未经检验')
count = validated_data["count"]
batch = validated_data["batch"]
mioitemw = validated_data.pop('mioitemw', [])
instance:MIOItem = super().create(validated_data)
assemb_dict = {}
for i in assemb:
assemb_dict[i['material'].id] = i
if material.is_assemb and '_in' in mio.type: # 仅入库且是组合件的时候需要填写下一级
components = material.components
for k, v in components.items():
if k in assemb_dict:
mia = assemb_dict[k]
MIOItemA.objects.create(
mioitem=instance, rate=v, **mia)
else:
raise ParseError('缺少组合件')
if material.tracking == Material.MA_TRACKING_SINGLE:
if len(mioitemw) == 0:
if mb:
wpr_qs = Wpr.get_qs_by_mb(mb)
if wpr_qs.count() == validated_data["count"]:
for item in wpr_qs:
MIOItemw.objects.create(mioitem=instance, number=item.number, wpr=item)
else:
raise ParseError('请提供产品明细编号')
elif wm:
wpr_qs = Wpr.get_qs_by_wm(wm)
if wpr_qs.count() == validated_data["count"]:
for item in wpr_qs:
MIOItemw.objects.create(mioitem=instance, number=item.number, wpr=item)
else:
raise ParseError('请提供产品明细编号')
elif mio.type in [MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_OTHER_IN] and count==1:
MIOItemw.objects.create(mioitem=instance, number=batch)
else:
raise ParseError('不支持自动生成请提供产品明细')
elif len(mioitemw) >= 1:
mio_type = mio.type
if mio_type != "pur_in" and mio_type != "other_in":
wprIds = [i["wpr"].id for i in mioitemw]
if instance.mb:
mb_ids = list(Wpr.objects.filter(id__in=wprIds).values_list("mb__id", flat=True).distinct())
if len(mb_ids) == 1 and mb_ids[0] == instance.mb.id:
pass
else:
raise ParseError(f'{batch}物料明细中存在{len(mb_ids)}个不同物料批次')
elif instance.wm:
wm_ids = list(Wpr.objects.filter(id__in=wprIds).values_list("wm__id", flat=True).distinct())
if len(wm_ids) == 1 and wm_ids[0] == instance.wm.id:
pass
else:
raise ParseError(f'{batch}物料明细中存在{len(wm_ids)}个不同物料批次')
for item in mioitemw:
if item.get("wpr", None) is None and mio_type != "pur_in" and mio_type != "other_in":
raise ParseError(f'{item["number"]}_请提供产品明细ID')
elif item.get("number_out", None) is not None and mio_type != MIO.MIO_TYPE_SALE_OUT:
raise ParseError(f'{item["number"]}_非销售出库不可赋予产品对外编号')
else:
MIOItemw.objects.create(mioitem=instance, **item)
return instance
class MIOItemAListSerializer(CustomModelSerializer):
material_ = MaterialSerializer(source='material', read_only=True)
material_name = serializers.StringRelatedField(
source='material', read_only=True)
class Meta:
model = MIOItemA
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE
class MIOItemListSimpleSerializer(CustomModelSerializer):
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
material_name = serializers.StringRelatedField(
source='material', read_only=True)
class Meta:
model = MIOItem
fields = ["id", "mio", "material", "warehouse", "material_name", "warehouse_name", "batch", "count", "test_date", "count_notok"]
class MIOItemSerializer(CustomModelSerializer):
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
material_ = MaterialSerializer(source='material', read_only=True)
assemb = serializers.SerializerMethodField(label="组合件信息")
material_name = serializers.StringRelatedField(
source='material', read_only=True)
inout_date = serializers.DateField(source='mio.inout_date', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True)
mioitemw = MIOItemwSerializer(many=True, required=False)
class Meta:
model = MIOItem
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
ret["price"] = None
if ret["unit_price"] is not None:
ret["price"] = Decimal(ret["count"]) * Decimal(ret["unit_price"])
return ret
def get_assemb(self, obj):
qs = MIOItemA.objects.filter(mioitem=obj)
if qs.exists():
return MIOItemAListSerializer(qs, many=True).data
elif obj.mb and obj.mb.material.is_assemb:
return MaterialBatchAListSerializer2(MaterialBatchA.objects.filter(mb=obj.mb), many=True).data
return None
class MioItemDetailSerializer(MIOItemSerializer):
mio_ = MIOListSerializer(source='mio', read_only=True)
class MIODoSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(
label="车间", queryset=Dept.objects.all(), required=False)
do_user = serializers.PrimaryKeyRelatedField(
label="执行人", queryset=User.objects.all(), required=True)
note = serializers.CharField(
required=False, allow_blank=True)
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'do_user',
'belong_dept', 'type', 'inout_date', 'mgroup', 'mio_user', 'type']
extra_kwargs = {'inout_date': {'required': True},
'do_user': {'required': True},
'number': {"required": False, "allow_blank": True},
'type': {'required': True}}
def validate(self, attrs):
if 'mgroup' in attrs and attrs['mgroup']:
attrs['belong_dept'] = attrs['mgroup'].belong_dept
if not attrs.get("belong_dept", None):
raise ParseError('请选择车间或工段')
return attrs
def create(self, validated_data):
type = validated_data['type']
if type in [MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_DO_OUT, MIO.MIO_TYPE_BORROW_OUT, MIO.MIO_TYPE_RETURN_IN]:
pass
else:
raise ValidationError('出入库类型错误')
if not validated_data.get("number", None):
validated_data["number"] = MIO.get_a_number(validated_data["type"])
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data.pop('type', None)
return super().update(instance, validated_data)
class MIOSaleSerializer(CustomModelSerializer):
# order = serializers.PrimaryKeyRelatedField(
# label="订单", queryset=Order.objects.all(), required=True)
note = serializers.CharField(
required=False, allow_blank=True)
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer', 'mio_user']
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
def create(self, validated_data):
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
order: Order = validated_data.get('order', None)
if not validated_data.get("number", None):
validated_data["number"] = MIO.get_a_number(validated_data["type"])
if order:
if order.state in [Order.ORDER_CREATE, Order.ORDER_DELIVERED]:
raise ValidationError('销售订单状态错误')
validated_data['customer'] = order.customer
if order.belong_dept:
validated_data['belong_dept'] = order.belong_dept
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data.pop('type', None)
return super().update(instance, validated_data)
class MIOPurSerializer(CustomModelSerializer):
# pu_order = serializers.PrimaryKeyRelatedField(
# label="采购订单", queryset=PuOrder.objects.all(), required=True)
note = serializers.CharField(
required=False, allow_blank=True)
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier', 'mio_user', 'type']
extra_kwargs = {'inout_date': {'required': True},
'number': {"required": False, "allow_blank": True},
'type': {'required': True}}
def create(self, validated_data):
type = validated_data["type"]
if type in [MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_PUR_OUT]:
pass
else:
raise ValidationError('出入库类型错误')
if not validated_data.get("number", None):
validated_data["number"] = MIO.get_a_number(validated_data["type"])
pu_order: PuOrder = validated_data.get('pu_order', None)
if pu_order:
if pu_order.state in [PuOrder.PUORDER_CREATE, PuOrder.PUORDER_DONE]:
raise ValidationError('采购订单状态错误')
validated_data['supplier'] = pu_order.supplier
if pu_order.belong_dept:
validated_data['belong_dept'] = pu_order.belong_dept
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data.pop('type', None)
return super().update(instance, validated_data)
class MIOOtherSerializer(CustomModelSerializer):
note = serializers.CharField(
required=False, allow_blank=True)
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'supplier',
'customer', 'type', 'inout_date', 'mio_user']
extra_kwargs = {'inout_date': {'required': True}, 'number': {"required": False, "allow_blank": True}}
def create(self, validated_data):
if not validated_data.get("number", None):
validated_data["number"] = MIO.get_a_number(validated_data["type"])
if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]:
raise ValidationError('出入库类型错误')
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data.pop('type', None)
return super().update(instance, validated_data)
class MIODetailSerializer(MIOListSerializer):
items = MIOItemSerializer(source='item_mio', many=True, read_only=True)
class MIOItemTestSerializer(CustomModelSerializer):
class Meta:
model = MIOItem
fields = ['id', 'test_date', 'test_user', 'count_notok', 'count_n_zw', 'count_n_tw',
'count_n_qp', 'count_n_wq', 'count_n_dl', 'count_n_pb', 'count_n_dxt', 'count_n_js', 'count_n_qx', 'count_n_zz', 'count_n_ysq', 'count_n_qt', 'count_n_hs', 'count_n_b', 'test_note']
extra_kwargs = {
'test_date': {'required': True},
'test_user': {'required': True}
}
def validate(self, attrs):
count_notok = 0
for i in attrs:
if 'count_n_' in i:
count_notok = count_notok + attrs[i]
attrs['count_notok'] = count_notok
return attrs
class MioItemAnaSerializer(serializers.Serializer):
start_date = serializers.DateField(label='开始日期', required=True)
end_date = serializers.DateField(label='结束日期', required=True)
material_cate = serializers.CharField(label='物料系列', required=True)
class MIOItemPurInTestSerializer(CustomModelSerializer):
class Meta:
model = MIOItem
fields = ['id', 'test_date', 'test_user',
'count_bag', 'count_sampling', 'weight_kgs', 'is_testok', 'test_note']
extra_kwargs = {
'test_date': {'required': True},
'test_user': {'required': True}
}
def validate(self, attrs):
if 'count_sampling' in attrs and attrs['count_sampling']:
pass
else:
weight_kgs = attrs['weight_kgs']
attrs['weight_kgs'] = [float(i) for i in weight_kgs]
attrs['count_sampling'] = len(attrs['weight_kgs'])
return super().validate(attrs)
class PackSerializer(CustomModelSerializer):
class Meta:
model = Pack
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE
def create(self, validated_data):
index = validated_data["index"]
mio = validated_data["mio"]
if Pack.objects.filter(mio=mio, index=index).exists():
raise ParseError('包装箱已存在')
return super().create(validated_data)
class PackMioSerializer(serializers.Serializer):
mioitems = serializers.ListField(child=serializers.CharField(), label="明细ID")
pack_index = serializers.IntegerField(label="包装箱序号")
# pack = serializers.CharField(label="包装箱ID")