1547 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			1547 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Python
		
	
	
	
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE
 | 
						||
from apps.utils.serializers import CustomModelSerializer
 | 
						||
from rest_framework import serializers
 | 
						||
from rest_framework.exceptions import ParseError
 | 
						||
from datetime import datetime
 | 
						||
 | 
						||
from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog, 
 | 
						||
                    Handover, Handoverb, Mlogb, AttLog,
 | 
						||
                     OtherLog, Fmlog, BatchSt, Mlogbw, Handoverbw, MlogbDefect, MlogUser, BatchLog)
 | 
						||
from apps.system.models import Dept, User
 | 
						||
from apps.system.serializers import UserSimpleSerializer
 | 
						||
from apps.pm.models import Mtask, Mtaskb
 | 
						||
from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_sec
 | 
						||
from apps.wpm.services import get_sflog, find_material_can_change, generate_new_batch
 | 
						||
from apps.mtm.models import Mgroup, TeamMember, Shift, Material, Route, Process
 | 
						||
from apps.mtm.serializers import MaterialSimpleSerializer
 | 
						||
from django.db import transaction
 | 
						||
from django.utils import timezone
 | 
						||
from django.core.cache import cache
 | 
						||
from django.utils.timezone import localdate
 | 
						||
from apps.qm.models import NotOkOption, Qct
 | 
						||
from apps.wf.serializers import TicketSimpleSerializer
 | 
						||
from apps.wpmw.models import Wpr
 | 
						||
from apps.qm.serializers import FtestProcessSerializer
 | 
						||
import logging
 | 
						||
from apps.qm.models import Defect
 | 
						||
from apps.utils.snowflake import idWorker
 | 
						||
from decimal import Decimal
 | 
						||
from apps.em.models import Equipment
 | 
						||
from django.db.models import Q
 | 
						||
mylogger = logging.getLogger("log")
 | 
						||
 | 
						||
class OtherLogSerializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = OtherLog
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS
 | 
						||
 | 
						||
 | 
						||
class StLogSerializer(CustomModelSerializer):
 | 
						||
    mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
 | 
						||
    current_sflog = serializers.CharField(label='当前处理值班', write_only=True)
 | 
						||
    current_note = serializers.CharField(
 | 
						||
        label='值班处理备注', write_only=True, allow_blank=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = StLog
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS + \
 | 
						||
            ['is_shutdown', 'sflog', 'duration']
 | 
						||
 | 
						||
    def create(self, validated_data):
 | 
						||
        current_sflog_id = validated_data.pop('current_sflog')
 | 
						||
        current_note = validated_data.pop('current_note', '')
 | 
						||
        sflog = get_sflog(
 | 
						||
            validated_data['mgroup'], validated_data['start_time'])
 | 
						||
        if current_sflog_id != sflog.id:
 | 
						||
            raise ParseError('值班时间与发生时间不一致')
 | 
						||
        instance = super().create(validated_data)
 | 
						||
        instance.sflog = sflog
 | 
						||
        instance.save()
 | 
						||
        SfLogExp.objects.create(
 | 
						||
            sflog=sflog, stlog=instance, create_by=self.context['request'].user, note=current_note)
 | 
						||
        return instance
 | 
						||
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        if instance.is_shutdown:  # 停机不可编辑end_time
 | 
						||
            validated_data.pop('end_time', None)
 | 
						||
        validated_data.pop('mgroup', None)
 | 
						||
        validated_data.pop('start_time', None)
 | 
						||
        end_time = validated_data.pop('end_time', None)
 | 
						||
        current_sflog = validated_data.pop('current_sflog')
 | 
						||
        current_note = validated_data.pop('current_note', '')
 | 
						||
        instance = super().update(instance, validated_data)
 | 
						||
        if end_time:  # 需要把涉及到的sflog都拉入
 | 
						||
            cal_exp_duration_sec(instance.id)
 | 
						||
        try:
 | 
						||
            sflogexp = SfLogExp.objects.get(
 | 
						||
                stlog=instance, sflog=current_sflog)
 | 
						||
            sflogexp.note = current_note
 | 
						||
            sflogexp.save()
 | 
						||
        except SfLogExp.DoesNotExist:
 | 
						||
            raise ParseError('该异常无需本班填写')
 | 
						||
        return instance
 | 
						||
 | 
						||
    def validate(self, attrs):
 | 
						||
        now = timezone.now()
 | 
						||
        start_time: datetime = attrs['start_time']
 | 
						||
        end_time: datetime = attrs.get('end_time', None)
 | 
						||
        if start_time > now:
 | 
						||
            raise ParseError('开始时间应为历史时间')
 | 
						||
        if end_time:
 | 
						||
            if end_time > now:
 | 
						||
                raise ParseError('开始时间应为历史时间')
 | 
						||
            if end_time > start_time:
 | 
						||
                attrs['duration_sec'] = (
 | 
						||
                    end_time - start_time).total_seconds()
 | 
						||
            else:
 | 
						||
                raise ParseError('结束时间应大于开始时间')
 | 
						||
        return super().validate(attrs)
 | 
						||
 | 
						||
 | 
						||
class SfLogSerializer(CustomModelSerializer):
 | 
						||
    mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
 | 
						||
    team_name = serializers.CharField(source='team.name', read_only=True)
 | 
						||
    shift_name = serializers.CharField(source='shift.name', read_only=True)
 | 
						||
    leader_name = serializers.CharField(source='leader.name', read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = SfLog
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS + \
 | 
						||
            ['mgroup', 'start_time', 'end_time', 'belong_dept']
 | 
						||
        extra_kwargs = {
 | 
						||
            'team': {'required': True},
 | 
						||
            'leader': {'required': True}
 | 
						||
        }
 | 
						||
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        old_pcoal_heat = instance.pcoal_heat
 | 
						||
        old_team = instance.team
 | 
						||
        instance: SfLog = super().update(instance, validated_data)
 | 
						||
        new_pcoal_heat = instance.pcoal_heat
 | 
						||
        new_team = instance.team
 | 
						||
        mgroup: Mgroup = instance.mgroup
 | 
						||
 | 
						||
        # 更新煤粉热值触发计算
 | 
						||
        if instance.mgroup.name == '回转窑' and instance.shift.name in ['早班', '白班']:
 | 
						||
            mylogger.info(f'回转窑白班/早班{instance.get_ymd}更新{new_pcoal_heat}-{old_pcoal_heat}')
 | 
						||
            year_s, month_s, day_s = instance.get_ymd
 | 
						||
            if new_pcoal_heat:  # 更新一下缓存
 | 
						||
                cache.set(f'pcoal_heat_{year_s}_{month_s}_{day_s}', new_pcoal_heat, timeout=60 * 60 * 48)
 | 
						||
            if new_pcoal_heat != old_pcoal_heat:
 | 
						||
                cal_enstat_when_pcoal_heat_change.delay(year_s, month_s, day_s)
 | 
						||
 | 
						||
        # 更新班组触发计算
 | 
						||
        if new_team != old_team:
 | 
						||
            default_state = 'pending'
 | 
						||
            if timezone.now() > instance.end_time:
 | 
						||
                default_state = 'normal'
 | 
						||
            # 分配班组时创建人员到岗情况
 | 
						||
            for item in TeamMember.objects.filter(team=new_team, mgroup=instance.mgroup):
 | 
						||
                AttLog.objects.get_or_create(sflog=instance, user=item.user, defaults={
 | 
						||
                                                'sflog': instance, 'user': item.user, 'post': item.post, 'state': default_state, 'create_by': self.context['request'].user})
 | 
						||
            if mgroup.need_enm:
 | 
						||
                from apps.qm.tasks import cal_quastat_sflog
 | 
						||
                cal_enstat_when_team_change.delay(instance.id)
 | 
						||
                cal_quastat_sflog.delay(instance.id)
 | 
						||
        return instance
 | 
						||
        
 | 
						||
    def to_representation(self, instance):
 | 
						||
        ret = super().to_representation(instance)
 | 
						||
        if "shut_sec" in ret:
 | 
						||
            ret["shut_hour"] = "{:.2f}".format(round(ret['shut_sec']/3600, 2))
 | 
						||
        return ret
 | 
						||
    # def to_internal_value(self, data):
 | 
						||
    #     if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']:
 | 
						||
    #         u_fields = self.Meta.update_fields
 | 
						||
    #         new_data = {key: data[key] for key in u_fields if key in data}
 | 
						||
    #         return super().to_internal_value(new_data)
 | 
						||
    #     return super().to_internal_value(data)
 | 
						||
 | 
						||
 | 
						||
class SflogExpSerializer(CustomModelSerializer):
 | 
						||
    # mgroup = serializers.CharField(source='sflog.mgroup.id', read_only=True)
 | 
						||
    # mgroup_name = serializers.CharField(
 | 
						||
    #     source='sflog.mgroup.name', read_only=True)
 | 
						||
    # stlog_ = StLogSerializer(source='stlog', read_only=True)
 | 
						||
    # happen_time = serializers.DateTimeField(required=True, label='发生时间')
 | 
						||
    # cate = serializers.CharField(required=True, label='原因类别')
 | 
						||
    sflog_ = SfLogSerializer(source='sflog', read_only=True)
 | 
						||
    stlog_ = StLogSerializer(source='stlog', read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = SfLogExp
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS + ['sflog', 'stlog', 'duration']
 | 
						||
 | 
						||
 | 
						||
class WMaterialSerializer(CustomModelSerializer):
 | 
						||
    material_ = MaterialSimpleSerializer(source='material', read_only=True)
 | 
						||
    supplier_name = serializers.CharField(source='supplier.name', read_only=True)
 | 
						||
    material_name = serializers.StringRelatedField(
 | 
						||
        source='material', read_only=True)
 | 
						||
    mgroup_name = serializers.StringRelatedField(source='mgroup.name', read_only=True)
 | 
						||
    belong_dept_name = serializers.CharField(
 | 
						||
        source='belong_dept.name', read_only=True)
 | 
						||
    material_ofrom_name = serializers.StringRelatedField(source='material_ofrom', read_only=True)
 | 
						||
    material_origin_name = serializers.StringRelatedField(source='material_origin', read_only=True)
 | 
						||
    notok_sign_name = serializers.SerializerMethodField()
 | 
						||
    defect_name = serializers.CharField(source="defect.name", read_only=True)
 | 
						||
    count_working = serializers.IntegerField(read_only=True, label='在制数量')
 | 
						||
    count_handovering = serializers.IntegerField(read_only=True, label='正在交送的数量')
 | 
						||
 | 
						||
    def get_notok_sign_name(self, obj):
 | 
						||
        return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = WMaterial
 | 
						||
        fields = '__all__'
 | 
						||
    
 | 
						||
    def to_representation(self, instance):
 | 
						||
        ret = super().to_representation(instance)
 | 
						||
        if 'count' in ret and 'count_working' in ret:
 | 
						||
            ret['count_cando'] = str(Decimal(ret['count']) - Decimal(ret['count_working']))
 | 
						||
        if 'count' in ret and 'count_handovering' in ret:
 | 
						||
            ret['count_canhandover'] = str(Decimal(ret['count']) - Decimal(ret['count_handovering']))
 | 
						||
        return ret
 | 
						||
 | 
						||
class MlogbDefectSerializer(CustomModelSerializer):
 | 
						||
    defect_name = serializers.CharField(source="defect.name", read_only=True)
 | 
						||
    defect_okcate = serializers.CharField(source="defect.okcate", read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = MlogbDefect
 | 
						||
        fields = ["id", "defect_name", "count", "mlogb", "defect", "defect_okcate", "count_has"]
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ["mlogb"]
 | 
						||
        extra_kwargs = {
 | 
						||
            'count_has': {'required': False},
 | 
						||
        }
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        if attrs["count"] < 0:
 | 
						||
            raise serializers.ValidationError("存在负数!")
 | 
						||
        if "count_has" not in attrs or attrs["count_has"] < attrs["count"]:
 | 
						||
            attrs["count_has"] = attrs["count"]
 | 
						||
        return attrs
 | 
						||
 | 
						||
class MlogbSerializer(CustomModelSerializer):
 | 
						||
    material_out_ = MaterialSimpleSerializer(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_out_name = serializers.StringRelatedField(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Mlogb
 | 
						||
        fields = ['id', 'material_out', 'count_ok',
 | 
						||
                  'material_out_', 'material_out_name', "mlog"]
 | 
						||
        extra_kwargs = {
 | 
						||
            'material_out': {'required': True, 'allow_null': False}}
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ["mlog"]
 | 
						||
        
 | 
						||
 | 
						||
class MlogListSerializer(CustomModelSerializer):
 | 
						||
    team_name = serializers.CharField(source="team.name", read_only=True)
 | 
						||
    mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True)
 | 
						||
    supplier_name = serializers.CharField(source='supplier.name', read_only=True)
 | 
						||
    # routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
 | 
						||
    belong_dept = serializers.CharField(
 | 
						||
        source='mgroup.belong_dept.id', read_only=True)
 | 
						||
    belong_dept_name = serializers.CharField(
 | 
						||
        source='mgroup.belong_dept.name', read_only=True)
 | 
						||
    mgroup_name = serializers.CharField(
 | 
						||
        source='mgroup.name', read_only=True)
 | 
						||
    mtask_count = serializers.IntegerField(
 | 
						||
        source='mtask.count', read_only=True)
 | 
						||
    create_by_name = serializers.CharField(
 | 
						||
        source='create_by.name', read_only=True)
 | 
						||
    update_by_name = serializers.CharField(
 | 
						||
        source='update_by.name', read_only=True)
 | 
						||
    # + 0.3s
 | 
						||
    material_in_ = MaterialSimpleSerializer(
 | 
						||
        source='material_in', read_only=True)
 | 
						||
    material_out_ = MaterialSimpleSerializer(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_out_name = serializers.StringRelatedField(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_in_name = serializers.StringRelatedField(
 | 
						||
        source='material_in', read_only=True)
 | 
						||
    #
 | 
						||
    handle_user_name = serializers.CharField(
 | 
						||
        source='handle_user.name', read_only=True)
 | 
						||
    handle_user_2_name = serializers.CharField(
 | 
						||
        source='handle_user_2.name', read_only=True)
 | 
						||
    handle_leader_name = serializers.CharField(
 | 
						||
        source='handle_leader.name', read_only=True)
 | 
						||
    equipment_name = serializers.StringRelatedField(
 | 
						||
        source='equipment', read_only=True)
 | 
						||
    equipment_2_name = serializers.StringRelatedField(
 | 
						||
        source='equipment_2', read_only=True)
 | 
						||
    shift_name = serializers.CharField(source='shift.name', read_only=True)
 | 
						||
    test_user_name = serializers.CharField(source='test_user.name', read_only=True)
 | 
						||
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
 | 
						||
    submit_user_name = serializers.CharField(source='submit_user.name', read_only=True)
 | 
						||
 | 
						||
    handle_users_ = UserSimpleSerializer(
 | 
						||
        source='handle_users', many=True, read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Mlog
 | 
						||
        fields = '__all__'
 | 
						||
        # exclude = ["equipments","handle_users", "material_outs"]
 | 
						||
 | 
						||
 | 
						||
class MlogbDetailSerializer(CustomModelSerializer):
 | 
						||
    material_out_name = serializers.StringRelatedField(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_in_name = serializers.StringRelatedField(source='material_in', read_only=True)
 | 
						||
    material_out_tracking = serializers.IntegerField(source="material_out.tracking", read_only=True)
 | 
						||
    mlogbdefect = MlogbDefectSerializer(many=True, read_only=True)
 | 
						||
    test_user_name = serializers.CharField(source='test_user.name', read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Mlogb
 | 
						||
        fields = '__all__'
 | 
						||
 | 
						||
class MlogSerializer(CustomModelSerializer):
 | 
						||
    team_name = serializers.CharField(source="team.name", read_only=True)
 | 
						||
    mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True)
 | 
						||
    supplier_name = serializers.CharField(source='supplier.name', read_only=True)
 | 
						||
    # routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
 | 
						||
    belong_dept = serializers.CharField(
 | 
						||
        source='mgroup.belong_dept.id', read_only=True)
 | 
						||
    belong_dept_name = serializers.CharField(
 | 
						||
        source='mgroup.belong_dept.name', read_only=True)
 | 
						||
    mgroup_name = serializers.CharField(
 | 
						||
        source='mgroup.name', read_only=True)
 | 
						||
    mtask_count = serializers.IntegerField(
 | 
						||
        source='mtask.count', read_only=True)
 | 
						||
    create_by_name = serializers.CharField(
 | 
						||
        source='create_by.name', read_only=True)
 | 
						||
    update_by_name = serializers.CharField(
 | 
						||
        source='update_by.name', read_only=True)
 | 
						||
    material_in_ = MaterialSimpleSerializer(
 | 
						||
        source='material_in', read_only=True)
 | 
						||
    material_out_ = MaterialSimpleSerializer(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_out_name = serializers.StringRelatedField(
 | 
						||
        source='material_out', read_only=True)
 | 
						||
    material_in_name = serializers.StringRelatedField(
 | 
						||
        source='material_in', read_only=True)
 | 
						||
    handle_user_name = serializers.CharField(
 | 
						||
        source='handle_user.name', read_only=True)
 | 
						||
    handle_user_2_name = serializers.CharField(
 | 
						||
        source='handle_user_2.name', read_only=True)
 | 
						||
    handle_leader_name = serializers.CharField(
 | 
						||
        source='handle_leader.name', read_only=True)
 | 
						||
    equipment_name = serializers.StringRelatedField(
 | 
						||
        source='equipment', read_only=True)
 | 
						||
    equipment_2_name = serializers.StringRelatedField(
 | 
						||
        source='equipment_2', read_only=True)
 | 
						||
    shift_name = serializers.CharField(source='shift.name', read_only=True)
 | 
						||
    mlogb = MlogbSerializer(
 | 
						||
        label='多产出件信息', many=True, required=False)
 | 
						||
    mlogb_full = MlogbDetailSerializer(
 | 
						||
        label='物料信息', many=True, read_only=True)
 | 
						||
    handle_users_ = UserSimpleSerializer(
 | 
						||
        source='handle_users', many=True, read_only=True)
 | 
						||
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
 | 
						||
    test_user_name = serializers.CharField(source='test_user.name', read_only=True)
 | 
						||
    submit_user_name = serializers.CharField(source='submit_user.name', read_only=True)
 | 
						||
 | 
						||
    mlogdefect = MlogbDefectSerializer(many=True, required=False)
 | 
						||
    mlogindefect = MlogbDefectSerializer(many=True, label="前道不良", required=False)
 | 
						||
    class Meta:
 | 
						||
        model = Mlog
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS + \
 | 
						||
            ['submit_time', 'submit_user', 'material_outs']
 | 
						||
        extra_kwargs = {
 | 
						||
           "batch": {"required": True},
 | 
						||
           "shift": {"required": False},
 | 
						||
           "material_out": {"required": True},
 | 
						||
           "work_start_time": {"required": False}
 | 
						||
        }
 | 
						||
 | 
						||
    def create(self, validated_data):
 | 
						||
        material_out = validated_data['material_out']
 | 
						||
        mtask:Mtask = validated_data.get('mtask', None)
 | 
						||
        mlogdefect = validated_data.pop('mlogdefect', None)
 | 
						||
        mlogbindefect = validated_data.pop('mlogindefect', None)
 | 
						||
        if mtask and mtask.state != Mtask.MTASK_ASSGINED:
 | 
						||
            raise ParseError('该任务非下达中不可选择')
 | 
						||
        
 | 
						||
        mlogb = validated_data.pop('mlogb', [])
 | 
						||
        instance: Mlog = super().create(validated_data)
 | 
						||
        ## 返工没有加工前不良
 | 
						||
        if instance.is_fix and instance.count_pn_jgqbl >0:
 | 
						||
            raise ParseError("返工不支持加工前不良")
 | 
						||
        # 自动生成mlogb
 | 
						||
        batch_in = instance.batch
 | 
						||
        wm_in = instance.wm_in
 | 
						||
        if wm_in:
 | 
						||
            batch_in = wm_in.batch
 | 
						||
            instance.batch = batch_in
 | 
						||
            instance.save(update_fields=['batch'])
 | 
						||
        mlogbin = None
 | 
						||
        if instance.material_in: # 如果有消耗
 | 
						||
            add_dict = {
 | 
						||
                'mlog': instance, 'batch': batch_in, 'wm_in': wm_in,
 | 
						||
                'mtask': instance.mtask, 'material_in': instance.material_in,
 | 
						||
                'route': instance.route,
 | 
						||
                'count_use': instance.count_use, 'count_break': instance.count_break, 
 | 
						||
                'count_pn_jgqbl': instance.count_pn_jgqbl
 | 
						||
            }
 | 
						||
            if wm_in:
 | 
						||
                add_dict['batch_ofrom'] = wm_in.batch_ofrom
 | 
						||
                add_dict['material_ofrom'] = wm_in.material_ofrom
 | 
						||
            mlogbin = Mlogb.objects.create(**add_dict)
 | 
						||
            if mlogbindefect is not None:
 | 
						||
                mlogbin_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": mlogbin, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogbindefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
                if mlogbin_defect_objects:
 | 
						||
                    MlogbDefect.objects.bulk_create(mlogbin_defect_objects)
 | 
						||
                mlogbin.cal_count_pn_jgqbl(cal_mlog=False)
 | 
						||
 | 
						||
        # mlogb只用于组合件输出物填写
 | 
						||
        brotherId_should_list = material_out.brothers
 | 
						||
        if brotherId_should_list:
 | 
						||
            if mlogb:
 | 
						||
                for item in mlogb:
 | 
						||
                    if item['material_out'].id in brotherId_should_list:
 | 
						||
                        mlogbx = Mlogb.objects.create(
 | 
						||
                            mlog=instance, 
 | 
						||
                            batch=instance.batch,
 | 
						||
                            route=instance.route, 
 | 
						||
                            mtask=instance.mtask, 
 | 
						||
                            material_out=item['material_out'], 
 | 
						||
                            count_ok=item['count_ok'])
 | 
						||
                        if wm_in:
 | 
						||
                            mlogbx.batch_ofrom = wm_in.batch_ofrom
 | 
						||
                            mlogbx.material_ofrom = wm_in.material_ofrom
 | 
						||
                            mlogbx.save(update_fields=["batch_ofrom", "material_ofrom"])
 | 
						||
            else:
 | 
						||
                raise ParseError('缺少产出物信息')
 | 
						||
        else:
 | 
						||
            # 生成产出物
 | 
						||
            batch_out = validated_data.get('batch', None)
 | 
						||
            if batch_out:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                batch_out = generate_new_batch(batch_in, instance)
 | 
						||
            
 | 
						||
            add_dict_2 = {
 | 
						||
                'mlog': instance, 'batch': batch_out,
 | 
						||
                'mtask': instance.mtask, 'material_out': instance.material_out,
 | 
						||
                'count_real': instance.count_real, 'route': instance.route,
 | 
						||
                'count_ok': instance.count_ok, 'count_notok': instance.count_notok, 
 | 
						||
                'count_break_t': instance.count_break_t, 
 | 
						||
                'qct': instance.qct
 | 
						||
            }
 | 
						||
            need_mdfect = False
 | 
						||
            if mlogdefect is not None:
 | 
						||
                need_mdfect = True
 | 
						||
            else:
 | 
						||
                for f in Mlogb._meta.fields:
 | 
						||
                        if 'count_n_' in f.name:
 | 
						||
                            add_dict_2[f.name] = getattr(instance, f.name)
 | 
						||
            ddict = {}
 | 
						||
            if wm_in:
 | 
						||
                ddict = {"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom}
 | 
						||
            if mlogbin:
 | 
						||
                ddict["mlogb_from"] = mlogbin
 | 
						||
            mlogb, _ = Mlogb.objects.get_or_create(**add_dict_2, defaults=ddict)
 | 
						||
            if need_mdfect:
 | 
						||
                mlogb_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": mlogb, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogdefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
                if mlogb_defect_objects:
 | 
						||
                    MlogbDefect.objects.bulk_create(mlogb_defect_objects)
 | 
						||
                mlogb.cal_count_notok(cal_mlog=False)
 | 
						||
        instance.cal_mlog_count_from_mlogb()
 | 
						||
        return instance
 | 
						||
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        if instance.fill_way == Mlog.MLOG_23:
 | 
						||
            raise ParseError('不支持的填写类型')
 | 
						||
        validated_data.pop('mtask', None)
 | 
						||
        validated_data.pop('mgroup', None)
 | 
						||
        mlogdefect = validated_data.pop('mlogdefect', None)
 | 
						||
        mlogbindefect = validated_data.pop('mlogindefect', None)
 | 
						||
        if instance.mtask:
 | 
						||
            validated_data.pop('handle_date', None)
 | 
						||
        # validated_data.pop('handle_user', None)
 | 
						||
 | 
						||
        mlogb = validated_data.pop('mlogb', [])
 | 
						||
        instance: Mlog = super().update(instance, validated_data)
 | 
						||
        ## 返工没有加工前不良
 | 
						||
        if instance.is_fix and instance.count_pn_jgqbl >0:
 | 
						||
            raise ParseError("返工不支持加工前不良")
 | 
						||
        wm_in = instance.wm_in
 | 
						||
        batch_in = instance.batch
 | 
						||
        if wm_in:
 | 
						||
            batch_in = wm_in.batch
 | 
						||
        # 修改消耗
 | 
						||
        if instance.fill_way in [Mlog.MLOG_12, Mlog.MLOG_2]:
 | 
						||
            # 自动生成mlogb
 | 
						||
            if instance.material_in:   # 有消耗的情况
 | 
						||
                minx, _ = Mlogb.objects.get_or_create(
 | 
						||
                    mlog=instance, 
 | 
						||
                    batch=batch_in, 
 | 
						||
                    wm_in=instance.wm_in,
 | 
						||
                    mtask=instance.mtask, 
 | 
						||
                    material_in=instance.material_in
 | 
						||
                )
 | 
						||
                if wm_in:
 | 
						||
                    minx.batch_ofrom = wm_in.batch_ofrom
 | 
						||
                    minx.material_ofrom = wm_in.material_ofrom
 | 
						||
                minx.count_use = instance.count_use
 | 
						||
                minx.count_break = instance.count_break
 | 
						||
                minx.count_pn_jgqbl = instance.count_pn_jgqbl
 | 
						||
                minx.qct = instance.qct
 | 
						||
                minx.save()
 | 
						||
                Mlogb.objects.filter(mlog=instance, material_in__isnull=False).exclude(id=minx.id).delete()
 | 
						||
                if mlogbindefect is not None:
 | 
						||
                    mlogbin_defect_objects = [
 | 
						||
                        MlogbDefect(**{**item, "mlogb": minx, "id": idWorker.get_id()})
 | 
						||
                        for item in mlogbindefect if item["count"] > 0
 | 
						||
                    ]
 | 
						||
                    if mlogbin_defect_objects:
 | 
						||
                        MlogbDefect.objects.bulk_create(mlogbin_defect_objects)
 | 
						||
                    minx.cal_count_pn_jgqbl(cal_mlog=False)
 | 
						||
        
 | 
						||
        # 修改产出
 | 
						||
        if instance.fill_way == Mlog.MLOG_2 and instance.material_out.brothers:
 | 
						||
            # 针对兄弟件的情况
 | 
						||
            Mlogb.objects.filter(mlog=instance, material_out__isnull=False).update(
 | 
						||
                batch=instance.batch,  # 注意mlog的batch有可能会进行修改
 | 
						||
                count_ok=0)
 | 
						||
            for item in mlogb:
 | 
						||
                Mlogb.objects.filter(mlog=instance, material_out=item['material_out']).update(
 | 
						||
                    batch=instance.batch,
 | 
						||
                    count_ok=item['count_ok'])
 | 
						||
                
 | 
						||
        elif instance.fill_way in [Mlog.MLOG_12, Mlog.MLOG_2]:
 | 
						||
            # 生成产出物
 | 
						||
            batch_out = instance.batch
 | 
						||
            if batch_out:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                batch_out = generate_new_batch(batch_in, instance)
 | 
						||
            mox, _ = Mlogb.objects.get_or_create(mlog=instance, batch=batch_out,
 | 
						||
                mtask=instance.mtask, material_out=instance.material_out)
 | 
						||
            # 需要同步更新数量
 | 
						||
            mox.count_real = instance.count_real
 | 
						||
            mox.count_ok = instance.count_ok
 | 
						||
            mox.count_notok = instance.count_notok
 | 
						||
            mox.count_break_t = instance.count_break_t
 | 
						||
            if wm_in:
 | 
						||
                mox.batch_ofrom = wm_in.batch
 | 
						||
                mox.material_ofrom = wm_in.material_ofrom
 | 
						||
            need_mdefect=False
 | 
						||
            if mlogdefect is not None:
 | 
						||
                need_mdefect = True
 | 
						||
            else:
 | 
						||
                for f in Mlogb._meta.fields:
 | 
						||
                    if 'count_n_' in f.name:
 | 
						||
                        setattr(mox, f.name, getattr(instance, f.name))
 | 
						||
            mox.save()
 | 
						||
            Mlogb.objects.filter(mlog=instance, material_out__isnull=False).exclude(id=mox.id).delete()
 | 
						||
            if need_mdefect:
 | 
						||
                MlogbDefect.objects.filter(mlogb__mlog=instance).delete()
 | 
						||
                mlogb_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": mox, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogdefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
                if mlogb_defect_objects:
 | 
						||
                    MlogbDefect.objects.bulk_create(mlogb_defect_objects)
 | 
						||
                mox.cal_count_notok(cal_mlog=False)
 | 
						||
        instance.cal_mlog_count_from_mlogb()
 | 
						||
        return instance
 | 
						||
 | 
						||
    def validate(self, attrs):
 | 
						||
        attrs['fill_way'] = Mlog.MLOG_2    # 只填第二级
 | 
						||
        attrs['mtype'] = Mlog.MTYPE_SELF   # 默认为自生产
 | 
						||
        fmlog:Fmlog = attrs.get('fmlog', None)
 | 
						||
        mtaskb = attrs.get('mtaskb', None)
 | 
						||
        wm_in: WMaterial = attrs.get('wm_in', None)
 | 
						||
        if fmlog:
 | 
						||
            attrs['fill_way'] = Mlog.MLOG_12
 | 
						||
            if not wm_in:
 | 
						||
                raise ParseError('未提供消耗的车间物料')
 | 
						||
            attrs['mgroup'] = fmlog.mgroup
 | 
						||
            attrs['is_fix'] = fmlog.is_fix
 | 
						||
            attrs['mtask'] = fmlog.mtask
 | 
						||
            attrs['route'] = fmlog.route
 | 
						||
            attrs['mtype'] = fmlog.mgroup.mtype
 | 
						||
            if attrs['mtask'] and attrs['mtask'].route:
 | 
						||
                attrs['route'] = attrs['mtask'].route
 | 
						||
            if mtaskb and mtaskb.mtask != fmlog.mtask:
 | 
						||
                raise ParseError('子任务不一致')
 | 
						||
        if wm_in:
 | 
						||
            attrs["material_in"] = wm_in.material
 | 
						||
            attrs["batch"] = wm_in.batch
 | 
						||
            if attrs["is_fix"]:
 | 
						||
                attrs["material_out"] = attrs["material_in"]
 | 
						||
                if wm_in.state in [WMaterial.WM_REPAIR, WMaterial.WM_NOTOK]:
 | 
						||
                    pass
 | 
						||
                else:
 | 
						||
                    raise ParseError('返修或复检需使用返修品/不合格品')
 | 
						||
            elif wm_in.state != WMaterial.WM_OK:
 | 
						||
                raise ParseError('非合格品不可使用')
 | 
						||
            if attrs['route'] and wm_in.material != attrs['route'].material_in:
 | 
						||
                raise ParseError('消耗物料与工艺步骤不一致')
 | 
						||
        if attrs['mtype']  == Mlog.MTYPE_OUT:
 | 
						||
            supplier = attrs.get('supplier', None)
 | 
						||
            if not supplier:
 | 
						||
                raise ParseError('外协必须选择外协单位')
 | 
						||
            
 | 
						||
        mtask = attrs.get('mtask', None)
 | 
						||
        mlogdefect = attrs.get('mlogdefect', None)
 | 
						||
        if mlogdefect is None:
 | 
						||
            count_notok = 0
 | 
						||
            for i in attrs:
 | 
						||
                if 'count_n_' in i:
 | 
						||
                    if attrs[i] < 0:
 | 
						||
                        raise ParseError(f'{attrs[i]}不能小于0')
 | 
						||
                    count_notok = count_notok + attrs[i]
 | 
						||
            attrs['count_notok'] = count_notok
 | 
						||
        if attrs['count_ok'] < 0:
 | 
						||
            raise ParseError('合格数量不能小于0')
 | 
						||
        if attrs['count_real'] != attrs['count_ok'] + attrs['count_notok']:
 | 
						||
            raise ParseError('生产数量需等于合格数量+不合格数量')
 | 
						||
        
 | 
						||
        mtaskb: Mtaskb = attrs.get('mtaskb', None)
 | 
						||
        if mtaskb:
 | 
						||
            mtask = mtaskb.mtask
 | 
						||
            attrs['mtask'] = mtask
 | 
						||
            attrs['handle_user'] = mtaskb.handle_user
 | 
						||
        if mtask:
 | 
						||
            attrs['mgroup'] = mtask.mgroup
 | 
						||
            attrs['material_in'] = mtask.material_in
 | 
						||
            material_out = mtask.material_out
 | 
						||
            if wm_in and wm_in.material != mtask.material_in:
 | 
						||
                raise ParseError('消耗物料与任务不一致')
 | 
						||
            attrs['material_out'] = material_out
 | 
						||
        else:
 | 
						||
            mgroup = attrs['mgroup']
 | 
						||
            material_out = attrs['material_out']
 | 
						||
            if not (mgroup and material_out):
 | 
						||
                raise ParseError('缺少工段或产物!')
 | 
						||
            
 | 
						||
        # 时间
 | 
						||
        mgroup:Mgroup = attrs['mgroup']
 | 
						||
        work_start_time:datetime = attrs.get('work_start_time', None)
 | 
						||
        if work_start_time:
 | 
						||
            attrs['handle_date'], attrs["shift"] = mgroup.get_shift(work_start_time)
 | 
						||
        else:
 | 
						||
            if "handle_date" in attrs and attrs["handle_date"]:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                raise ParseError('缺少生产日期')
 | 
						||
        if mtask and mtask.start_date == mtask.end_date:
 | 
						||
            if attrs['handle_date'] != mtask.end_date:
 | 
						||
                if work_start_time:
 | 
						||
                    raise ParseError('任务日期与生产日期不一致')
 | 
						||
                else:
 | 
						||
                    attrs['handle_date'] = mtask.end_date
 | 
						||
 | 
						||
        handle_user = attrs.get('handle_user', None)
 | 
						||
        if handle_user is None and hasattr(self, "request"):
 | 
						||
            handle_user = self.request.user
 | 
						||
        return attrs
 | 
						||
 | 
						||
 | 
						||
class MlogInitSerializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = Mlog
 | 
						||
        fields = ['id', 
 | 
						||
        'work_start_time', 'work_end_time', 'mgroup', 'reminder_interval_list', 
 | 
						||
        'route', 'equipment', 'handle_user', 'note', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json', 'is_fix', "handle_users", "team", "handle_date"]
 | 
						||
        read_only_fields = ["mtype"]
 | 
						||
        extra_kwargs = {
 | 
						||
            'work_start_time': {'required': True},
 | 
						||
            'route':{'required': False},
 | 
						||
            'mgroup': {'required': True},
 | 
						||
            'mtype': {'required': True}
 | 
						||
        }
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        mgroup: Mgroup = attrs['mgroup']
 | 
						||
        is_fix:bool = attrs.get('is_fix', False)
 | 
						||
        attrs['mtype'] = mgroup.mtype
 | 
						||
        if is_fix:
 | 
						||
            attrs["route"] = None
 | 
						||
            attrs['hour_work'] = None
 | 
						||
            attrs['material_in'] = None
 | 
						||
            attrs['material_out'] = None
 | 
						||
        else:
 | 
						||
            route: Route = attrs.get('route', None)
 | 
						||
            # if not route:
 | 
						||
            #     raise ParseError('缺少工艺路线')
 | 
						||
            if route:
 | 
						||
                if route and route.process != mgroup.process:
 | 
						||
                    raise ParseError('工序不匹配')
 | 
						||
                attrs['hour_work'] = route.hour_work
 | 
						||
                attrs['material_in'] = route.material_in
 | 
						||
                attrs['material_out'] = route.material_out
 | 
						||
        attrs['fill_way'] = Mlog.MLOG_23
 | 
						||
        if attrs['mtype']  == Mlog.MTYPE_OUT:
 | 
						||
            supplier = attrs.get('supplier', None)
 | 
						||
            if not supplier:
 | 
						||
                raise ParseError('外协必须选择外协单位')
 | 
						||
        # 如果已经确定产出,则自动获取qct
 | 
						||
        if attrs.get("material_out", None):
 | 
						||
            attrs["qct"] = Qct.get(attrs["material_out"], "process", "out")
 | 
						||
 | 
						||
        attrs["handle_date"], attrs["shift"] = mgroup.get_shift(attrs['work_start_time'])
 | 
						||
        return attrs
 | 
						||
    
 | 
						||
class MlogChangeSerializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = Mlog
 | 
						||
        fields = ['id', 'work_start_time', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time', 'equipment', "team"]
 | 
						||
    
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        work_start_time = validated_data.get('work_start_time', None)
 | 
						||
        if work_start_time:
 | 
						||
            mgroup:Mgroup = instance.mgroup
 | 
						||
            validated_data["handle_date"], validated_data["shift"] = mgroup.get_shift(work_start_time)
 | 
						||
        return super().update(instance, validated_data)
 | 
						||
 | 
						||
 | 
						||
class CountJsonSerializer(serializers.Serializer):
 | 
						||
    count_use = serializers.IntegerField(min_value=0, required=True)
 | 
						||
    floor = serializers.IntegerField(min_value=1, required=True)
 | 
						||
    count_test = serializers.IntegerField(min_value=0, required=True)
 | 
						||
    count_notok = serializers.IntegerField(min_value=0, required=True)
 | 
						||
 | 
						||
class CountJsonFromSerializer(serializers.Serializer):
 | 
						||
    start = serializers.IntegerField(min_value=0, required=True)
 | 
						||
    end = serializers.IntegerField(min_value=0, required=True)
 | 
						||
    count_t = serializers.IntegerField(min_value=0, required=True, label="总数")
 | 
						||
 | 
						||
class MlogbInSerializer(CustomModelSerializer):
 | 
						||
    mlogbdefect = MlogbDefectSerializer(many=True, required=False)
 | 
						||
    count_json_from = CountJsonFromSerializer(required=False, many=True)
 | 
						||
    wprs_in = serializers.ListField(child=serializers.CharField(), label="单个ID的列表", required=False)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Mlogb
 | 
						||
        fields = ['id', 'mlog', 'mtask', 'route', 'wm_in', 'count_use', 'count_pn_jgqbl',
 | 
						||
                  'count_break', 'note', "parent", "mlogbdefect", "count_json_from", "wprs_in"]
 | 
						||
        extra_kwargs = {'count_use': {'required': True}, 'mtask': {'required': False},
 | 
						||
                        'wm_in': {'required': True, "allow_empty": False}}
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        if attrs["count_use"] < 0 or attrs.get("count_pn_jgqbl", 0) < 0 or attrs.get("count_break", 0) < 0:
 | 
						||
            raise ParseError('存在负数!')
 | 
						||
        mlog:Mlog = attrs['mlog']
 | 
						||
        is_fix = mlog.is_fix
 | 
						||
        mtask: Mtask = attrs.get("mtask", None)
 | 
						||
        if mtask and mtask.state != Mtask.MTASK_ASSGINED:
 | 
						||
            raise ParseError('该任务非下达中不可选择')
 | 
						||
        wm_in: WMaterial = attrs['wm_in']
 | 
						||
        if wm_in is None:
 | 
						||
            raise ParseError("请选择相应车间库存!")
 | 
						||
        if is_fix: # 返修或复检
 | 
						||
            if wm_in.state in [WMaterial.WM_REPAIR, WMaterial.WM_NOTOK]:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                raise ParseError('返修或复检需使用返修品/不合格品')
 | 
						||
        elif wm_in.state != WMaterial.WM_OK:
 | 
						||
            raise ParseError('非合格品不可使用')
 | 
						||
        
 | 
						||
        route = None
 | 
						||
        mlog_route = mlog.route
 | 
						||
        if mlog_route is None:
 | 
						||
            if is_fix is False:
 | 
						||
                route = attrs.get("route", None)
 | 
						||
                if not route:
 | 
						||
                    raise ParseError("缺少工艺步骤")
 | 
						||
            else:
 | 
						||
                attrs["route"] = route
 | 
						||
        else:
 | 
						||
            route = mlog_route
 | 
						||
            attrs["route"] = route
 | 
						||
        
 | 
						||
        if mtask and route != mtask.route:
 | 
						||
            raise ParseError('工艺步骤与任务不匹配')
 | 
						||
        attrs['material_in'] = wm_in.material
 | 
						||
        attrs['batch'] = wm_in.batch
 | 
						||
        attrs["batch_ofrom"] = wm_in.batch_ofrom
 | 
						||
        attrs["material_ofrom"] = wm_in.material_ofrom
 | 
						||
        attrs["number_from"] = wm_in.number_from
 | 
						||
        if route and route.batch_bind and mtask is not None:
 | 
						||
            if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists():
 | 
						||
                raise ParseError('该车间库存非本任务使用')
 | 
						||
        return attrs
 | 
						||
 | 
						||
    def create(self, validated_data):
 | 
						||
        mlog: Mlog = validated_data['mlog']
 | 
						||
        mtask: Mtask = validated_data.get("mtask", None)
 | 
						||
        mlogbdefect = validated_data.pop("mlogbdefect", None)
 | 
						||
        wprs_in = validated_data.pop("wprs_in", [])
 | 
						||
        if Mlogb.objects.filter(mlog=mlog, mtask=mtask, wm_in=validated_data['wm_in'], parent=None).exists():
 | 
						||
            raise ParseError('该记录已存在')
 | 
						||
        if mlog.submit_time is not None:
 | 
						||
            raise ParseError('生产日志已提交不可编辑')
 | 
						||
 | 
						||
        ins:Mlogb = super().create(validated_data)
 | 
						||
        validated_data["wprs_in"] = wprs_in
 | 
						||
        if mlogbdefect is not None and ins.material_in.tracking == Material.MA_TRACKING_BATCH:
 | 
						||
            mlogb_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": ins, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogbdefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
            if mlogb_defect_objects:
 | 
						||
                MlogbDefect.objects.bulk_create(mlogb_defect_objects)
 | 
						||
            ins.cal_count_pn_jgqbl(cal_mlog=False)
 | 
						||
        return ins
 | 
						||
 | 
						||
class MlogbInUpdateSerializer(CustomModelSerializer):
 | 
						||
    mlogbdefect = MlogbDefectSerializer(many=True, required=False)
 | 
						||
    class Meta:
 | 
						||
        model = Mlogb
 | 
						||
        fields = ['id', 'count_use', 'count_pn_jgqbl', 'note', 'mlogbdefect', "need_inout"]
 | 
						||
 | 
						||
    def validate(self, attrs):
 | 
						||
        if attrs["count_use"] < 0 or attrs.get("count_pn_jgqbl", 0) < 0:
 | 
						||
            raise ParseError('存在负数!')
 | 
						||
        return attrs
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        mlog: Mlog =  instance.mlog
 | 
						||
        mlogbdefect = validated_data.pop("mlogbdefect", None)
 | 
						||
        if mlog.submit_time is not None:
 | 
						||
            raise ParseError('生产日志已提交不可编辑')
 | 
						||
        ins:Mlogb = super().update(instance, validated_data)
 | 
						||
        if mlogbdefect is not None and ins.material_in.tracking == Material.MA_TRACKING_BATCH:
 | 
						||
            MlogbDefect.objects.filter(mlogb=ins).delete()
 | 
						||
            mlogb_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": ins, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogbdefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
            if mlogb_defect_objects:
 | 
						||
                MlogbDefect.objects.bulk_create(mlogb_defect_objects)
 | 
						||
            ins.cal_count_pn_jgqbl(cal_mlog=False)
 | 
						||
            # 只有普通工序的才可联动
 | 
						||
            route:Route = ins.route if ins.route else ins.mlog.route
 | 
						||
            if route and route.process and route.process.mtype == Process.PRO_NORMAL:
 | 
						||
                if route.material_out.tracking == Material.MA_TRACKING_BATCH:
 | 
						||
                    mlogbout_qs = Mlogb.objects.filter(mlog=ins.mlog, mlogb_from=ins)
 | 
						||
                    if mlogbout_qs.count() == 1:
 | 
						||
                        mlogbout = mlogbout_qs.first()
 | 
						||
                        mlogbout.count_real = ins.count_use - ins.count_pn_jgqbl - ins.count_break
 | 
						||
                        mlogbout.count_ok = mlogbout.count_real - mlogbout.count_notok
 | 
						||
                        if mlogbout.count_real < 0 or mlogbout.count_ok < 0:
 | 
						||
                            raise ParseError("对应的产出数异常")
 | 
						||
                        mlogbout.save(update_fields=["count_real", "count_ok"])
 | 
						||
                        mlogbout.cal_count_notok(cal_mlog=False)
 | 
						||
        return ins
 | 
						||
    
 | 
						||
class MlogbwCreateUpdateSerializer(CustomModelSerializer):
 | 
						||
    ftest = FtestProcessSerializer(required=False, allow_null=True)
 | 
						||
    equip_name = serializers.CharField(source='equip.name', read_only=True)
 | 
						||
    equip_number = serializers.CharField(source='equip.number', read_only=True)
 | 
						||
    wpr_number_out = serializers.CharField(source='wpr.number_out', read_only=True)
 | 
						||
    mlogb__batch = serializers.CharField(source='mlogb.batch', read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = Mlogbw
 | 
						||
        fields = ["id", "number", "wpr", "note", 
 | 
						||
                  "mlogb", "ftest", "equip", "work_start_time", 
 | 
						||
                  "work_end_time", "mlogbw_from", "equip_name", "equip_number", "wpr_number_out", "mlogb__batch"]
 | 
						||
        read_only_fields = ["mlogbw_from"]
 | 
						||
 | 
						||
    def validate(self, attrs):
 | 
						||
        mlogb:Mlogb = attrs["mlogb"]
 | 
						||
        if mlogb.mlog.submit_time is not None:
 | 
						||
            raise ParseError('生产日志已提交不可编辑')
 | 
						||
        wpr:Wpr = attrs.get("wpr", None)
 | 
						||
        in_or_out, tracking = mlogb.get_tracking()
 | 
						||
        if tracking != Material.MA_TRACKING_SINGLE:
 | 
						||
            raise ParseError('非单件追踪不可使用')
 | 
						||
        if in_or_out == 'in':
 | 
						||
            if not wpr:
 | 
						||
                raise ParseError('请选择相应产品')
 | 
						||
            if mlogb.wm_in and wpr.wm != mlogb.wm_in:
 | 
						||
                raise ParseError(f'{wpr.number}-该产品非本批次')
 | 
						||
        return attrs
 | 
						||
 | 
						||
    def save_ftest(self, mlogbw, ftest_data):
 | 
						||
        ftest = mlogbw.ftest
 | 
						||
        if not ftest:
 | 
						||
            ftest_sr = FtestProcessSerializer(data=ftest_data)
 | 
						||
            ftest = ftest_sr.create(ftest_data)
 | 
						||
            mlogbw.ftest = ftest
 | 
						||
            mlogbw.save()
 | 
						||
        else:
 | 
						||
            ftest_sr = FtestProcessSerializer()
 | 
						||
            ftest_sr.update(instance=ftest, validated_data=ftest_data)
 | 
						||
        return mlogbw
 | 
						||
 | 
						||
    def create(self, validated_data):
 | 
						||
        wpr: Wpr = validated_data.get("wpr", None)
 | 
						||
        if wpr:
 | 
						||
            mlogb: Mlogb = validated_data["mlogb"]
 | 
						||
            if Mlogbw.objects.filter(mlogb=mlogb, wpr=wpr).exists():
 | 
						||
                raise ParseError(f'{wpr.number}-该产品已选入')
 | 
						||
            if Mlogbw.objects.filter(mlogb__mlog__submit_time__isnull=True, wpr=wpr).exists():
 | 
						||
                raise ParseError(f'{wpr.number}-该产品已在其他日志中选入')
 | 
						||
        ftest_data = validated_data.pop("ftest", None)
 | 
						||
        mlogbw = super().create(validated_data)
 | 
						||
        if ftest_data:
 | 
						||
            mlogbw = self.save_ftest(mlogbw, ftest_data)
 | 
						||
        return mlogbw
 | 
						||
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        validated_data.pop("mlogb")
 | 
						||
        ftest_data = validated_data.pop("ftest", None)
 | 
						||
        mlogbw:Mlogbw = super().update(instance, validated_data)
 | 
						||
        if ftest_data:
 | 
						||
            mlogbw = self.save_ftest(mlogbw, ftest_data)
 | 
						||
        elif ftest_data is None:
 | 
						||
            ftest = mlogbw.ftest
 | 
						||
            if ftest:
 | 
						||
                mlogbw.ftest = None
 | 
						||
                mlogbw.save()
 | 
						||
                ftest.delete()
 | 
						||
        return mlogbw
 | 
						||
 | 
						||
 | 
						||
class MlogbwStartTestSerializer(serializers.Serializer):
 | 
						||
    mlogbw_ids = serializers.ListField(child=serializers.CharField(), label="mlogbwId列表")
 | 
						||
    test_equip = serializers.CharField(label="测试设备", allow_null=True, required=False, allow_blank=True)
 | 
						||
    test_user = serializers.CharField(label="测试人员")
 | 
						||
    defects = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="缺陷项列表")
 | 
						||
    testitems = serializers.ListField(child=serializers.CharField(), required=False, allow_null=True, label="测试项列表")
 | 
						||
    test_date = serializers.DateField(label="测试日期")
 | 
						||
    qct = serializers.CharField(label="检测表id")
 | 
						||
 | 
						||
    def save(self, **kwargs):
 | 
						||
        from apps.qm.models import Ftest, FtestDefect, FtestItem
 | 
						||
        
 | 
						||
        validated_data = self.validated_data
 | 
						||
        mlogbw_ids = validated_data["mlogbw_ids"]
 | 
						||
        test_equip_id = validated_data.get("test_equip")
 | 
						||
        test_user_id = validated_data["test_user"]
 | 
						||
        defect_ids = validated_data.get("defects", [])
 | 
						||
        testitem_ids = validated_data.get("testitems", [])
 | 
						||
        test_date = validated_data["test_date"]
 | 
						||
        qct_id = validated_data["qct"]
 | 
						||
 | 
						||
        # 预加载相关对象
 | 
						||
        test_equip = Equipment.objects.get(id=test_equip_id) if test_equip_id else None
 | 
						||
        test_user = User.objects.get(id=test_user_id)
 | 
						||
        qct = Qct.objects.get(id=qct_id)
 | 
						||
        
 | 
						||
        # 批量获取所有mlogbw对象
 | 
						||
        mlogbws = Mlogbw.objects.filter(id__in=mlogbw_ids).select_related('ftest')
 | 
						||
        
 | 
						||
        # 预加载缺陷和测试项
 | 
						||
        defects = Defect.objects.filter(id__in=defect_ids) if defect_ids else []
 | 
						||
        testitems = FtestItem.objects.filter(id__in=testitem_ids) if testitem_ids else []
 | 
						||
        
 | 
						||
        existing_ftests = {}
 | 
						||
        new_ftests = []
 | 
						||
        mlogbws_to_update = []
 | 
						||
        
 | 
						||
        # 分离已存在和需要新建的ftest
 | 
						||
        for mlogbw in mlogbws:
 | 
						||
            if mlogbw.ftest:
 | 
						||
                existing_ftests[mlogbw.ftest_id] = mlogbw.ftest
 | 
						||
            else:
 | 
						||
                ftest = Ftest(test_date=test_date, qct=qct, test_user=test_user, type="process", id=idWorker.get_id(), is_ok=True)
 | 
						||
                new_ftests.append(ftest)
 | 
						||
                mlogbws_to_update.append(mlogbw)
 | 
						||
 | 
						||
        # 批量创建新的ftest
 | 
						||
        if new_ftests:
 | 
						||
            Ftest.objects.bulk_create(new_ftests)
 | 
						||
            
 | 
						||
            # 更新mlogbw的ftest关系
 | 
						||
            for mlogbw, ftest in zip(mlogbws_to_update, new_ftests):
 | 
						||
                mlogbw.ftest = ftest
 | 
						||
            Mlogbw.objects.bulk_update(mlogbws_to_update, ['ftest'])
 | 
						||
            
 | 
						||
            # 将新创建的ftest添加到existing_ftests中
 | 
						||
            for ftest in new_ftests:
 | 
						||
                existing_ftests[ftest.id] = ftest
 | 
						||
 | 
						||
        # 批量处理缺陷项(需要更新test_user)
 | 
						||
        if defects and existing_ftests:
 | 
						||
            # 获取所有现有的FtestDefect记录
 | 
						||
            existing_defect_ids = FtestDefect.objects.filter(
 | 
						||
                ftest_id__in=existing_ftests.keys(),
 | 
						||
                defect_id__in=[d.id for d in defects]
 | 
						||
            ).values_list('defect_id', 'ftest_id')
 | 
						||
            
 | 
						||
            existing_defect_map = {(defect_id, ftest_id) for defect_id, ftest_id in existing_defect_ids}
 | 
						||
            
 | 
						||
            defects_to_create = []
 | 
						||
            defects_to_update = []
 | 
						||
            
 | 
						||
            for ftest in existing_ftests.values():
 | 
						||
                for defect in defects:
 | 
						||
                    if (defect.id, ftest.id) in existing_defect_map:
 | 
						||
                        # 已有记录,需要更新
 | 
						||
                        defects_to_update.append((ftest.id, defect.id))
 | 
						||
                    else:
 | 
						||
                        # 新记录,需要创建
 | 
						||
                        defects_to_create.append(
 | 
						||
                            FtestDefect(ftest=ftest, defect=defect, test_user=test_user, id=idWorker.get_id())
 | 
						||
                        )
 | 
						||
            
 | 
						||
            # 批量创建新记录
 | 
						||
            if defects_to_create:
 | 
						||
                FtestDefect.objects.bulk_create(defects_to_create)
 | 
						||
            
 | 
						||
            # 批量更新已有记录的test_user
 | 
						||
            if defects_to_update:
 | 
						||
                FtestDefect.objects.filter(
 | 
						||
                    ftest_id__in=[item[0] for item in defects_to_update],
 | 
						||
                    defect_id__in=[item[1] for item in defects_to_update]
 | 
						||
                ).update(test_user=test_user)
 | 
						||
 | 
						||
        # 批量处理测试项(需要更新test_user和test_equip)
 | 
						||
        if testitems and existing_ftests:
 | 
						||
            # 获取所有现有的FtestItem记录
 | 
						||
            existing_testitem_ids = FtestItem.objects.filter(
 | 
						||
                ftest_id__in=existing_ftests.keys(),
 | 
						||
                testitem_id__in=[t.id for t in testitems]
 | 
						||
            ).values_list('testitem_id', 'ftest_id')
 | 
						||
            
 | 
						||
            existing_testitem_map = {(testitem_id, ftest_id) for testitem_id, ftest_id in existing_testitem_ids}
 | 
						||
            
 | 
						||
            testitems_to_create = []
 | 
						||
            testitems_to_update_condition = Q()
 | 
						||
            
 | 
						||
            for ftest in existing_ftests.values():
 | 
						||
                for testitem in testitems:
 | 
						||
                    if (testitem.id, ftest.id) in existing_testitem_map:
 | 
						||
                        # 已有记录,添加到更新条件
 | 
						||
                        testitems_to_update_condition |= Q(ftest=ftest, testitem=testitem)
 | 
						||
                    else:
 | 
						||
                        # 新记录,需要创建
 | 
						||
                        testitems_to_create.append(
 | 
						||
                            FtestItem(
 | 
						||
                                ftest=ftest, 
 | 
						||
                                testitem=testitem, 
 | 
						||
                                test_user=test_user, 
 | 
						||
                                test_equip=test_equip,
 | 
						||
                                id=idWorker.get_id()
 | 
						||
                            )
 | 
						||
                        )
 | 
						||
            
 | 
						||
            # 批量创建新记录
 | 
						||
            if testitems_to_create:
 | 
						||
                FtestItem.objects.bulk_create(testitems_to_create)
 | 
						||
            
 | 
						||
            # 批量更新已有记录的test_user和test_equip
 | 
						||
            if testitems_to_update_condition:
 | 
						||
                FtestItem.objects.filter(testitems_to_update_condition).update(
 | 
						||
                    test_user=test_user,
 | 
						||
                    test_equip=test_equip
 | 
						||
                )
 | 
						||
 | 
						||
class MlogbOutUpdateSerializer(CustomModelSerializer):
 | 
						||
    mlogbdefect = MlogbDefectSerializer(many=True, required=False)
 | 
						||
    count_json = CountJsonSerializer(required=False, many=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Mlogb
 | 
						||
        fields = "__all__"
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out',
 | 
						||
                                                  'count_use', 'count_break', 'count_pn_jgqbl', 'mlogbdefect', "qct", "count_json"]
 | 
						||
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        mlog:Mlog = instance.mlog
 | 
						||
        if mlog.submit_time is not None:
 | 
						||
            raise ParseError('生产日志已提交不可编辑')
 | 
						||
        mlogbdefect = validated_data.pop("mlogbdefect", None)
 | 
						||
 | 
						||
        ins:Mlogb = super().update(instance, validated_data)
 | 
						||
        if ins.need_inout is False:
 | 
						||
            if ins.mlogb_from:
 | 
						||
                if Mlogb.objects.filter(mlog=ins.mlog, material_out__isnull=False, mlogb_from=ins.mlogb_from).count() == 1:
 | 
						||
                    ins_from = ins.mlogb_from
 | 
						||
                    ins_from.need_inout = False
 | 
						||
                    ins_from.save(update_fields=["need_inout"])
 | 
						||
                else:
 | 
						||
                    raise ParseError("对应消耗的产出有多个, 需手动指定消耗是否出库")
 | 
						||
            else:
 | 
						||
                raise ParseError("该产出需入库!")
 | 
						||
        else:
 | 
						||
            if ins.mlogb_from:
 | 
						||
                ins_from = ins.mlogb_from
 | 
						||
                ins_from.need_inout = True
 | 
						||
                ins_from.save(update_fields=["need_inout"])
 | 
						||
        if mlogbdefect is not None and ins.material_out.tracking == Material.MA_TRACKING_BATCH:
 | 
						||
            MlogbDefect.objects.filter(mlogb=ins).delete()
 | 
						||
            mlogb_defect_objects = [
 | 
						||
                    MlogbDefect(**{**item, "mlogb": ins, "id": idWorker.get_id()})
 | 
						||
                    for item in mlogbdefect if item["count"] > 0
 | 
						||
                ]
 | 
						||
            if mlogb_defect_objects:
 | 
						||
                MlogbDefect.objects.bulk_create(mlogb_defect_objects)
 | 
						||
            ins.cal_count_notok(cal_mlog=False)
 | 
						||
        return ins
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        mlogbdefect = attrs.get("mlogbdefect", [])
 | 
						||
        if mlogbdefect:
 | 
						||
            attrs.pop("count_notok_json", None)
 | 
						||
            for k in list(attrs.keys()):
 | 
						||
                if 'count_n_' in k:
 | 
						||
                    attrs.pop(k, None)
 | 
						||
        else:
 | 
						||
            count_notok_json = attrs.get('count_notok_json', [])
 | 
						||
            # count_notok_json字段处理
 | 
						||
            if count_notok_json:
 | 
						||
                # 先置0字段
 | 
						||
                for k in list(attrs.keys()):
 | 
						||
                    if 'count_n_' in k:
 | 
						||
                        attrs[k] = 0
 | 
						||
                count_notok_dict = {}
 | 
						||
                for item in count_notok_json:
 | 
						||
                    notok = item['notok']
 | 
						||
                    count = item['count']
 | 
						||
                    full_notok = f'count_n_{notok}'
 | 
						||
                    if not hasattr(Mlogb, full_notok):
 | 
						||
                        raise ParseError(f'{notok}-该不合格项不存在')
 | 
						||
                    if full_notok in count_notok_dict:
 | 
						||
                        count_notok_dict[full_notok] = count_notok_dict[full_notok] + count
 | 
						||
                    else:
 | 
						||
                        count_notok_dict[full_notok] = count
 | 
						||
                for k, v in count_notok_dict.items():
 | 
						||
                    attrs[k] = v
 | 
						||
 | 
						||
            count_notok = 0
 | 
						||
            for k in list(attrs.keys()):
 | 
						||
                if 'count_n_' in k:
 | 
						||
                    if not hasattr(Mlogb, k):
 | 
						||
                        raise ParseError(f'{k}不存在')
 | 
						||
                    count_notok = count_notok + attrs[k]
 | 
						||
            attrs['count_notok'] = count_notok
 | 
						||
            if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                raise ParseError('生产数量不能小于合格数量')
 | 
						||
        return attrs
 | 
						||
 | 
						||
class MlogRevertSerializer(serializers.Serializer):
 | 
						||
    change_reason = serializers.CharField(label='撤回原因')
 | 
						||
 | 
						||
 | 
						||
class MlogRelatedSerializer(serializers.Serializer):
 | 
						||
    mtask = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='小任务ID', queryset=Mtask.objects.all())
 | 
						||
 | 
						||
 | 
						||
class DeptBatchSerializer(serializers.Serializer):
 | 
						||
    belong_dept_name = serializers.CharField(label='车间名称')
 | 
						||
 | 
						||
class Handoverbwserializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = Handoverbw
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ["handoverb", "number"]
 | 
						||
        extra_kwargs = {'wpr': {'required': True}}
 | 
						||
 | 
						||
class HandoverbSerializer(CustomModelSerializer):
 | 
						||
    notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
 | 
						||
    defect_name = serializers.CharField(source="wm.defect.name", read_only=True)
 | 
						||
    handoverbw = Handoverbwserializer(many=True, required=False)
 | 
						||
    class Meta:
 | 
						||
        model = Handoverb
 | 
						||
        fields = "__all__"
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ['handover']
 | 
						||
        extra_kwargs = {'wm': {'required': True}}
 | 
						||
 | 
						||
class HandoverbListSerializer(CustomModelSerializer):
 | 
						||
    defect_name = serializers.CharField(source="wm.defect.name", read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = Handoverb
 | 
						||
        fields = "__all__"
 | 
						||
 | 
						||
class HandoverSerializer(CustomModelSerializer):
 | 
						||
    # wm = serializers.PrimaryKeyRelatedField(
 | 
						||
    #     label='车间库存ID', queryset=WMaterial.objects.all())
 | 
						||
    # material = serializers.PrimaryKeyRelatedField(
 | 
						||
    #     required=True, label='物料ID', queryset=Material.objects.all())
 | 
						||
    send_user_name = serializers.CharField(
 | 
						||
        source='send_user.name', read_only=True)
 | 
						||
    recive_user_name = serializers.CharField(
 | 
						||
        source='recive_user.name', read_only=True)
 | 
						||
    recive_dept_name = serializers.CharField(
 | 
						||
        source='recive_dept', read_only=True)
 | 
						||
    send_mgroup_name = serializers.CharField(source='send_mgroup.name', read_only=True)
 | 
						||
    recive_mgroup_name = serializers.CharField(source='recive_mgroup.name', read_only=True)
 | 
						||
    material_ = MaterialSimpleSerializer(source='material', read_only=True)
 | 
						||
    material_name = serializers.StringRelatedField(
 | 
						||
        source='material', read_only=True)
 | 
						||
    wm_notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
 | 
						||
    handoverb = HandoverbSerializer(many=True, required=False)
 | 
						||
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        if "mtype" not in attrs:
 | 
						||
            attrs['mtype'] = Handover.H_NORMAL
 | 
						||
        mtype = attrs["mtype"]
 | 
						||
        if 'type' not in attrs:
 | 
						||
            attrs['type'] = Handover.H_NORMAL
 | 
						||
        if attrs["type"] == Handover.H_CHANGE:
 | 
						||
            if "material_changed" not in attrs:
 | 
						||
                raise ParseError("必须指定改版后的物料")
 | 
						||
            
 | 
						||
        if mtype == Handover.H_MERGE:
 | 
						||
            new_state = None
 | 
						||
            new_wm:WMaterial = attrs.get("new_wm", None)
 | 
						||
            if new_wm:
 | 
						||
                attrs['new_batch'] = new_wm.batch
 | 
						||
                new_state = new_wm.state
 | 
						||
                new_defect = new_wm.defect
 | 
						||
            if not attrs.get("new_batch", None):
 | 
						||
                raise ParseError("必须指定合并后的批次")
 | 
						||
                
 | 
						||
            
 | 
						||
        wm:WMaterial = attrs.get('wm', None)
 | 
						||
        if wm:
 | 
						||
            attrs["batch"] = wm.batch
 | 
						||
        handoverb = attrs.get('handoverb', [])
 | 
						||
        if wm and not handoverb:
 | 
						||
            attrs['handoverb'] = [{"wm": wm, "count": attrs["count"] }]
 | 
						||
            handoverb = attrs['handoverb']
 | 
						||
            attrs['batch'] = wm.batch
 | 
						||
        elif handoverb:
 | 
						||
            wm: WMaterial = handoverb[0]["wm"]
 | 
						||
        if wm:
 | 
						||
            pass
 | 
						||
        else:
 | 
						||
            raise ParseError('必须指定车间库存')
 | 
						||
        attrs['material'] = wm.material
 | 
						||
        attrs['send_dept'] = wm.belong_dept
 | 
						||
        if wm.mgroup:
 | 
						||
            attrs['send_mgroup'] = wm.mgroup
 | 
						||
        if attrs['material'].process and attrs['material'].process.into_wm_mgroup and 'recive_mgroup' not in attrs:
 | 
						||
            raise ParseError('必须指定交接工段')
 | 
						||
        if 'recive_mgroup' in attrs and attrs['recive_mgroup']:
 | 
						||
            attrs['recive_dept'] = attrs['recive_mgroup'].belong_dept
 | 
						||
        if not attrs.get('recive_dept', None) and not attrs.get('recive_mgroup', None):
 | 
						||
            raise ParseError('必须指定收料车间或收料工段')
 | 
						||
        if not attrs.get('send_dept', None) and not attrs.get('send_mgroup', None):
 | 
						||
            raise ParseError('必须指定送料车间或送料工段')
 | 
						||
        
 | 
						||
        if attrs["mtype"] == Handover.H_NORMAL and attrs.get("recive_mgroup", None) == attrs.get("send_mgroup", None):
 | 
						||
            raise ParseError('正常交接收料工段与送料工段不能相同')
 | 
						||
        t_count = 0
 | 
						||
        tracking = attrs["material"].tracking
 | 
						||
        for ind, item in enumerate(attrs['handoverb']):
 | 
						||
            if item["count"] > 0:
 | 
						||
                pass
 | 
						||
            else:
 | 
						||
                raise ParseError(f'第{ind+1}行-交接数量必须大于0')
 | 
						||
            wm = item["wm"]
 | 
						||
            if mtype == Handover.H_MERGE:
 | 
						||
                if new_state is None:
 | 
						||
                    new_mat = wm.material
 | 
						||
                    new_state = wm.state
 | 
						||
                    new_defect = wm.defect
 | 
						||
                else:
 | 
						||
                    if new_mat != wm.material:
 | 
						||
                        raise ParseError(f'第{ind+1}行-合并的物料不一致')
 | 
						||
                    if new_state != wm.state:
 | 
						||
                        raise ParseError(f'第{ind+1}行-合并的物料状态不一致')
 | 
						||
                    if new_defect != wm.defect:
 | 
						||
                        raise ParseError(f'第{ind+1}行-合并的物料缺陷不一致')
 | 
						||
            if tracking == Material.MA_TRACKING_SINGLE:
 | 
						||
                handoverbw = item.get("handoverbw", [])
 | 
						||
                if handoverbw:
 | 
						||
                    item["count"] = len(handoverbw)
 | 
						||
                    t_count += len(handoverbw)
 | 
						||
                    wprIds = [i["wpr"].id for i in handoverbw]
 | 
						||
                    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] == wm.id:
 | 
						||
                        pass
 | 
						||
                    else:
 | 
						||
                        raise ParseError(f'{wm.batch}物料明细中存在{len(wm_ids)}个不同物料批次')
 | 
						||
                    
 | 
						||
                elif wm.count == item["count"]:
 | 
						||
                    t_count += item["count"]
 | 
						||
                else:
 | 
						||
                    raise ParseError(f'第{ind+1}行-请提供交接物料明细编号')
 | 
						||
            else:
 | 
						||
                t_count += item["count"]
 | 
						||
            # if wm.mgroup and wm.mgroup != attrs['send_mgroup']:
 | 
						||
            #     raise ParseError(f'第{ind+1}物料与交接工段不一致')
 | 
						||
            # if wm.belong_dept and wm.belong_dept != attrs['send_dept']:
 | 
						||
            #     raise ParseError(f'第{ind+1}物料与交接部门不一致')
 | 
						||
            if attrs["material"] != wm.material:
 | 
						||
                raise ParseError(f'第{ind+1}物料与交接物料不一致')
 | 
						||
            # if wm.state != WMaterial.WM_OK  and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]:
 | 
						||
            #     raise ParseError(f'第{ind+1}物料不合格,不能进行正常/检验交接')
 | 
						||
            if wm.count_xtest is not None:
 | 
						||
                raise ParseError(f'第{ind+1}物料检验中,不能进行交接')
 | 
						||
        attrs["count"] = t_count
 | 
						||
        if attrs['type'] == Handover.H_REPAIR:
 | 
						||
            # 返修时还是该物料
 | 
						||
            recive_mgroup = attrs.get("recive_mgroup", None)
 | 
						||
            if recive_mgroup is None:
 | 
						||
                raise ParseError('返工交接需指定工段')
 | 
						||
        return attrs
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = Handover
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS + ["mlog"]
 | 
						||
        extra_kwargs = {
 | 
						||
            "type": {"required": False},
 | 
						||
            "wm": {"required": False},
 | 
						||
            "send_dept": {"required": False},
 | 
						||
            "recive_mgroup": {"required": False},
 | 
						||
            "recive_dept": {"required": False},
 | 
						||
            "material": {"required": False},
 | 
						||
            "material_changed": {"required": False},
 | 
						||
            "batch": {"required": False},
 | 
						||
            "count": {"required": False},
 | 
						||
            "count_eweight": {"required": False}
 | 
						||
        }
 | 
						||
    
 | 
						||
    def create(self, validated_data):
 | 
						||
        handoverb = validated_data.pop('handoverb', [])
 | 
						||
        ins = super().create(validated_data)
 | 
						||
        mtype = validated_data["mtype"]
 | 
						||
        for ind, item in enumerate(handoverb):
 | 
						||
            wm = item["wm"]
 | 
						||
            count = item["count"]
 | 
						||
            handoverbw = item.pop("handoverbw", [])
 | 
						||
            if mtype == Handover.H_DIV:
 | 
						||
                if not item["batch"]:
 | 
						||
                    raise ParseError(f'第{ind+1}行-请提供拆批批次号')
 | 
						||
                if ins.wm is None:
 | 
						||
                    raise ParseError("请提供拆批库存")
 | 
						||
                handoverb = Handoverb.objects.create(handover=ins, batch=item["batch"], count=count, wm=ins.wm)
 | 
						||
            else:
 | 
						||
                handoverb = Handoverb.objects.create(handover=ins, wm=wm, count=count, batch=wm.batch)
 | 
						||
            if wm.material.tracking == Material.MA_TRACKING_SINGLE:
 | 
						||
                if handoverbw:
 | 
						||
                    handoverb.count = len(handoverbw)
 | 
						||
                    handoverb.save()
 | 
						||
                    for item in handoverbw:
 | 
						||
                        wpr = item["wpr"]
 | 
						||
                        Handoverbw.objects.get_or_create(wpr=wpr, handoverb=handoverb, defaults={"number":wpr.number, "note": item.get("note", None)})
 | 
						||
                elif count == wm.count:
 | 
						||
                    for item in Wpr.get_qs_by_wm(wm):
 | 
						||
                        Handoverbw.objects.get_or_create(wpr=item, handoverb=handoverb, defaults={"number":item.number})
 | 
						||
                else:
 | 
						||
                    raise ParseError(f'第{ind+1}行-请提供交接物料明细编号')
 | 
						||
        return ins
 | 
						||
    
 | 
						||
    def update(self, instance, validated_data):
 | 
						||
        handoverb = validated_data.pop('handoverb', [])
 | 
						||
        
 | 
						||
        insx = super().update(instance, validated_data)
 | 
						||
        Handoverb.objects.filter(handover=instance).delete()
 | 
						||
        for ind, item in enumerate(handoverb):
 | 
						||
            wm = item["wm"]
 | 
						||
            count = item["count"]
 | 
						||
            handoverbw = item.pop("handoverbw", [])
 | 
						||
            if validated_data["mtype"] == Handover.H_DIV:
 | 
						||
                if not item["batch"]:
 | 
						||
                    raise ParseError(f'第{ind+1}行-请提供拆批批次号')
 | 
						||
                if insx.wm is None:
 | 
						||
                    raise ParseError("请提供拆批库存")
 | 
						||
                hb, _ = Handoverb.objects.get_or_create(handover=instance, batch=item["batch"], wm=insx.wm,
 | 
						||
                                                        defaults={"count": count})
 | 
						||
            else:
 | 
						||
                hb, _ = Handoverb.objects.get_or_create(handover=instance, wm=wm, defaults={"count": count,
 | 
						||
                "batch": wm.batch})
 | 
						||
            if wm.material.tracking == Material.MA_TRACKING_SINGLE:
 | 
						||
                Handoverbw.objects.filter(handoverb=hb).delete()
 | 
						||
                if handoverbw:
 | 
						||
                    hb.count = len(handoverbw)
 | 
						||
                    hb.save()
 | 
						||
                    for item in handoverbw:
 | 
						||
                        wpr = item["wpr"]
 | 
						||
                        Handoverbw.objects.get_or_create(wpr=wpr, handoverb=hb, defaults={"number": wpr.number, "note": item.get("note", None)})
 | 
						||
                elif count == wm.count:
 | 
						||
                    wpr_qs = Wpr.get_qs_by_wm(wm)
 | 
						||
                    for item in wpr_qs:
 | 
						||
                        Handoverbw.objects.get_or_create(wpr=item, handoverb=handoverb,
 | 
						||
                                                            defaults={"number": item.number})
 | 
						||
                else:
 | 
						||
                    raise ParseError(f'第{ind+1}行-请提供交接物料明细')
 | 
						||
        return instance
 | 
						||
 | 
						||
class HandoverUpdateSerializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = Handover
 | 
						||
        fields = ['id', 'send_date', 'send_user', 'count', 'count_eweight', 'recive_user', 'note']
 | 
						||
 | 
						||
class HandoverListSerializer(HandoverSerializer):
 | 
						||
    handoverb = HandoverbListSerializer(many=True, required=False)
 | 
						||
 | 
						||
 | 
						||
class GenHandoverSerializer(serializers.Serializer):
 | 
						||
    mlogs = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='mlog的ID列表', queryset=Mlog.objects.all(), many=True)
 | 
						||
    recive_dept = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='交送车间', queryset=Dept.objects.all())
 | 
						||
    recive_user = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='接收人', queryset=User.objects.all())
 | 
						||
    send_user = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='交送人', queryset=User.objects.all())
 | 
						||
    send_date = serializers.DateField(label='交送日期')
 | 
						||
 | 
						||
 | 
						||
class GenHandoverWmSerializer(serializers.Serializer):
 | 
						||
    wm = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='车间物料ID', queryset=WMaterial.objects.all())
 | 
						||
    send_mgroup = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='送料工段ID', queryset=Mgroup.objects.all())
 | 
						||
    recive_dept = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='交送车间', queryset=Dept.objects.all())
 | 
						||
    recive_user = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='接收人', queryset=User.objects.all())
 | 
						||
    send_user = serializers.PrimaryKeyRelatedField(
 | 
						||
        label='交送人', queryset=User.objects.all())
 | 
						||
    send_date = serializers.DateField(label='交送日期')
 | 
						||
    count = serializers.IntegerField(label='交送数量')
 | 
						||
 | 
						||
    def validate(self, attrs):
 | 
						||
        if attrs['count'] <= 1:
 | 
						||
            raise ParseError('交送数量必须大于1')
 | 
						||
        return attrs
 | 
						||
 | 
						||
 | 
						||
class MlogAnaSerializer(serializers.Serializer):
 | 
						||
    belong_dept_name = serializers.CharField(label='车间名', required=False)
 | 
						||
    start_date = serializers.DateField(label='开始日期', required=True)
 | 
						||
    end_date = serializers.DateField(label='结束日期', required=True)
 | 
						||
    material_cate = serializers.CharField(label='物料系列', required=False)
 | 
						||
 | 
						||
 | 
						||
class AttLogSerializer(CustomModelSerializer):
 | 
						||
    mgroup_name = serializers.CharField(
 | 
						||
        source='sflog.mgroup.name', read_only=True)
 | 
						||
    user_name = serializers.CharField(source='user.name', read_only=True)
 | 
						||
    post_name = serializers.CharField(source='post.name', read_only=True)
 | 
						||
    belong_dept_name = serializers.CharField(
 | 
						||
        source='sflog.mgroup.belong_dept.name', read_only=True)
 | 
						||
 | 
						||
    class Meta:
 | 
						||
        model = AttLog
 | 
						||
        fields = '__all__'
 | 
						||
 | 
						||
 | 
						||
class FmlogSerializer(CustomModelSerializer):
 | 
						||
    # routepack_name = serializers.CharField(
 | 
						||
    #     source='mtask.route.routepack.name', read_only=True)
 | 
						||
    route_name = serializers.StringRelatedField(source="route", read_only=True)
 | 
						||
    mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
 | 
						||
    mtask_number = serializers.CharField(source='mtask.number', read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = Fmlog
 | 
						||
        fields = '__all__'
 | 
						||
        read_only_fields = EXCLUDE_FIELDS
 | 
						||
    
 | 
						||
    def validate(self, attrs):
 | 
						||
        is_fix = attrs.get("is_fix", False)
 | 
						||
        if is_fix:
 | 
						||
            attrs["mtask"] = None
 | 
						||
            attrs['route'] = None
 | 
						||
        elif not attrs.get("mtask", None) and not attrs.get("route", None):
 | 
						||
            raise ParseError("请选择任务或工艺步骤")
 | 
						||
        mtask: Mtask = attrs['mtask']
 | 
						||
        if mtask and mtask.state != Mtask.MTASK_ASSGINED:
 | 
						||
            raise ParseError('该任务非下达中不可选择')
 | 
						||
        if mtask:
 | 
						||
            attrs["route"] = mtask.route
 | 
						||
        return attrs
 | 
						||
 | 
						||
class FmlogUpdateSerializer(CustomModelSerializer):
 | 
						||
    class Meta:
 | 
						||
        model = Fmlog
 | 
						||
        fields = ['id', 'note', 'enabled']
 | 
						||
 | 
						||
 | 
						||
class MlogTCreateSerializer(CustomModelSerializer):
 | 
						||
    pass
 | 
						||
 | 
						||
 | 
						||
class BatchStSerializer(CustomModelSerializer):
 | 
						||
    material_start_name = serializers.CharField(source='material_start.name', read_only=True)
 | 
						||
    material_start_model = serializers.CharField(source='material_start.model', read_only=True)
 | 
						||
    material_start_specification = serializers.CharField(source='material_start.specification', read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = BatchSt
 | 
						||
        fields = "__all__"
 | 
						||
 | 
						||
 | 
						||
class HandoverMgroupSerializer(serializers.Serializer):
 | 
						||
    material = serializers.CharField(label="物料ID")
 | 
						||
    type = serializers.IntegerField(label="交送类型")
 | 
						||
 | 
						||
 | 
						||
class MlogUserSerializer(CustomModelSerializer):
 | 
						||
    handle_user_name = serializers.CharField(source='handle_user.name', read_only=True)
 | 
						||
    process_name = serializers.CharField(source='process.name', read_only=True)
 | 
						||
    shift_name = serializers.CharField(source='shift.name', read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = MlogUser
 | 
						||
        fields = "__all__"
 | 
						||
        read_only_fields = EXCLUDE_FIELDS_BASE + ["shift", "handle_date"]
 | 
						||
        extra_kwargs = {
 | 
						||
            "work_start_time": {"required": True}
 | 
						||
        }
 | 
						||
 | 
						||
    def create(self, validated_data):
 | 
						||
        mlog:Mlog = validated_data["mlog"]
 | 
						||
        work_start_time:datetime = validated_data["work_start_time"]
 | 
						||
        if mlog.work_start_time < mlog.work_start_time:
 | 
						||
            raise ParseError("操作时间不能早于日志开始时间")
 | 
						||
        validated_data["handle_date"], validated_data["shift"] = mlog.mgroup.get_shift(work_start_time)
 | 
						||
        if mlog.submit_time is not None:
 | 
						||
            raise ParseError("该日志已提交")
 | 
						||
        process:Process = validated_data["process"]
 | 
						||
        if mlog.route:
 | 
						||
            p = mlog.route.process
 | 
						||
            if process.parent != p:
 | 
						||
                raise ParseError("该步骤不匹配")
 | 
						||
        else:
 | 
						||
            raise ParseError("该日志没有工艺步骤")
 | 
						||
        if MlogUser.objects.filter(mlog=mlog, process=process).exists():
 | 
						||
            raise ParseError("该工序已选择")
 | 
						||
        return super().create(validated_data)
 | 
						||
 | 
						||
 | 
						||
class BatchLogSerializer(CustomModelSerializer):
 | 
						||
    source_batch = serializers.CharField(source='source.batch', read_only=True)
 | 
						||
    target_batch = serializers.CharField(source='target.batch', read_only=True)
 | 
						||
    class Meta:
 | 
						||
        model = BatchLog
 | 
						||
        fields = "__all__"
 | 
						||
 | 
						||
 | 
						||
class BatchMgroupSerializer(serializers.Serializer):
 | 
						||
    # mgroup = serializers.PrimaryKeyRelatedField(
 | 
						||
    #     label='工段', queryset=Mgroup.objects.all())
 | 
						||
    mgroup_name = serializers.CharField(label="工段名")
 | 
						||
 | 
						||
 | 
						||
class MlogQuickSerializer(serializers.Serializer):
 | 
						||
    work_start_time = serializers.DateTimeField(label="开始时间")
 | 
						||
    # handle_date = serializers.DateField(label="操作日期")
 | 
						||
    work_end_time = serializers.DateTimeField(label="结束时间", required=False)
 | 
						||
    team = serializers.CharField(label="班组ID", required=False)
 | 
						||
    equipment = serializers.CharField(label="设备ID", required=False)
 | 
						||
    wm_in = serializers.CharField(label="输入车间库存ID")
 | 
						||
    wprs_in = serializers.ListField(child=serializers.CharField(), label="单个ID的列表", required=False)
 | 
						||
    count_use = serializers.IntegerField(label="领用数量")
 | 
						||
    is_fix = serializers.BooleanField(label="是否返修")
 | 
						||
    mgroup = serializers.CharField(label="工段ID")
 | 
						||
    route = serializers.CharField(label="工艺步骤ID", required=False)
 | 
						||
    mtask = serializers.CharField(label="任务ID", required=False)
 | 
						||
    handle_user = serializers.CharField(label="操作人员ID") |