458 lines
19 KiB
Python
458 lines
19 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 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)
|
|
|
|
class Meta:
|
|
model = MIO
|
|
fields = '__all__'
|
|
read_only_fields = EXCLUDE_FIELDS + \
|
|
['state', 'submit_time', 'submit_user', 'number']
|
|
|
|
|
|
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"]
|
|
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 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('该批次的物料未经检验')
|
|
with transaction.atomic():
|
|
count = validated_data["count"]
|
|
batch = validated_data["batch"]
|
|
mioitemw = validated_data.pop('mioitemw', [])
|
|
instance = 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
|
|
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 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") |