from apps.utils.serializers import CustomModelSerializer from apps.mtm.models import (Shift, Material, Mgroup, Team, Goal, Process, Route, TeamMember, RoutePack, Srule, RouteMat) from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS_DEPT from rest_framework import serializers from rest_framework.exceptions import ValidationError, ParseError from apps.system.models import Dept, UserPost from django.db import transaction from apps.wf.serializers import TicketSimpleSerializer from apps.system.serializers import FileSerializer class ShiftSerializer(CustomModelSerializer): class Meta: model = Shift fields = '__all__' read_only_fields = EXCLUDE_FIELDS class MaterialSimpleSerializer(CustomModelSerializer): full_name = serializers.SerializerMethodField() process_name = serializers.CharField(source='process.name', read_only=True) class Meta: model = Material fields = ['id', 'name', 'number', 'model', 'specification', 'type', 'cate', 'brothers', 'process_name', 'full_name', "tracking", "bin_number_main"] def get_full_name(self, obj): return f'{obj.name}|{obj.specification if obj.specification else ""}|{obj.model if obj.model else ""}|{obj.process.name if obj.process else ""}' class MaterialSerializer(CustomModelSerializer): process_name = serializers.CharField(source='process.name', read_only=True) full_name = serializers.SerializerMethodField() class Meta: model = Material fields = '__all__' read_only_fields = EXCLUDE_FIELDS + ['count'] def get_full_name(self, obj): return f'{obj.name}|{obj.specification if obj.specification else ""}|{obj.model if obj.model else ""}|{obj.process.name if obj.process else ""}' def create(self, validated_data): if Material.objects.filter(name=validated_data['name'], specification=validated_data.get('specification', None), model=validated_data.get('model', None), process=validated_data.get('process', None)).exists(): raise serializers.ValidationError('物料已存在') return super().create(validated_data) def validate(self, attrs): type = attrs['type'] if type in [Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]: if attrs.get('process', None) is None: raise ValidationError('半成品/成品必须指定所到工序') return super().validate(attrs) class MgroupSerializer(CustomModelSerializer): belong_dept = serializers.PrimaryKeyRelatedField( label="所属部门", queryset=Dept.objects.all(), required=True) belong_dept_name = serializers.CharField( source='belong_dept.name', read_only=True) process_name = serializers.CharField(source='process.name', read_only=True) process_type = serializers.CharField(source='process.type', read_only=True) process_cate = serializers.CharField(source='process.cate', read_only=True) class Meta: model = Mgroup fields = '__all__' read_only_fields = EXCLUDE_FIELDS class TeamMemberSerializer(CustomModelSerializer): user_name = serializers.CharField(source='user.name', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) post_name = serializers.CharField(source='post.name', read_only=True) class Meta: model = TeamMember fields = '__all__' read_only_fields = EXCLUDE_FIELDS_BASE def validate(self, attrs): user = attrs['user'] post = attrs.get('post', None) if post is None: userpost = UserPost.objects.filter( dept=attrs['mgroup'].belong_dept, user=user).first() if userpost: attrs['post'] = userpost.post else: raise ParseError(f'{user.name}未配置岗位') return super().validate(attrs) class TeamSerializer(CustomModelSerializer): belong_dept = serializers.PrimaryKeyRelatedField( label="所属部门", queryset=Dept.objects.all(), required=True) leader_name = serializers.CharField(source='leader.name', read_only=True) belong_dept_name = serializers.CharField( source='belong_dept.name', read_only=True) class Meta: model = Team fields = '__all__' read_only_fields = EXCLUDE_FIELDS class GoalSerializer(CustomModelSerializer): goal_cate_name = serializers.CharField( source='goal_cate.name', read_only=True) goal_cate_code = serializers.CharField(source='goal_cate.code', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) class Meta: model = Goal fields = '__all__' read_only_fields = EXCLUDE_FIELDS class MgroupGoalYearSerializer(serializers.Serializer): mgroup = serializers.CharField(label='工段ID') year = serializers.IntegerField(label='年') class ProcessSerializer(CustomModelSerializer): belong_dept_name = serializers.CharField( source='belong_dept.name', read_only=True) class Meta: model = Process fields = '__all__' read_only_fields = EXCLUDE_FIELDS class RoutePackSerializer(CustomModelSerializer): material_name = serializers.StringRelatedField( source='material', read_only=True) create_by_name = serializers.CharField(source='create_by.name', read_only=True) ticket_ = TicketSimpleSerializer(source='ticket', read_only=True) document_ = FileSerializer(source='document', read_only=True) class Meta: model = RoutePack fields = '__all__' read_only_fields = EXCLUDE_FIELDS + ['state', 'ticket'] # def update(self, instance, validated_data): # if validated_data['material'] != instance.material: # raise ParseError('不可变更产品') # return super().update(instance, validated_data) class RoutePackCopySerializer(serializers.Serializer): routepack = serializers.CharField(label='工艺包ID') new_name = serializers.CharField(label='新名称') material_in = serializers.CharField(label='原料ID') material_out = serializers.CharField(label='产品ID') class RouteSerializer(CustomModelSerializer): name = serializers.CharField(source='__str__', read_only=True) material_ = MaterialSerializer(source='material', read_only=True) routepack_name = serializers.StringRelatedField(source='routepack.name', read_only=True) process_name = serializers.CharField(source='process.name', read_only=True) process_cate = serializers.CharField(source='process.cate', read_only=True) material_in_name = serializers.StringRelatedField( source='material_in', read_only=True) material_out_name = serializers.StringRelatedField( source='material_out', read_only=True) material_out_type = serializers.CharField( source='material_out.type', read_only=True) material_out_is_hidden = serializers.BooleanField( source='material_out.is_hidden', read_only=True) material_out_tracking = serializers.IntegerField(write_only=True, required=False, allow_null=True) class Meta: model = Route fields = '__all__' read_only_fields = EXCLUDE_FIELDS def validate(self, attrs): if attrs.get('routepack', None): attrs['material'] = attrs['routepack'].material if 'mgroup' in attrs and attrs['mgroup']: attrs['process'] = attrs['mgroup'].process process: Process = attrs.get('process', None) if process is None: raise ParseError('未提供操作工序') if process.parent is not None: raise ParseError('操作工序不可为子工序') if process.mtype == Process.PRO_DIV and attrs.get('div_number', 1) < 1: raise ParseError('切分数量必须大于等于1') return super().validate(attrs) @classmethod def gen_material_out(cls, instance: Route, material_out_tracking:int, user=None): """ 自动形成物料 """ material = instance.material process = instance.process material_out: Material = Material.objects.get_queryset(all=True).filter(type=Material.MA_TYPE_HALFGOOD, parent=material, process=process).first() if material_out: material_out.is_deleted = False if material_out.parent == material: material_out.name = material.name material_out.model = material.model material_out.specification = material.specification material_out.cate = material.cate material_out.tracking = material_out_tracking material_out.save() return material_out material_out = Material.objects.get_queryset(all=True).filter(name=material.name, model=material.model, process=process, specification=material.specification).first() if material_out: material_out.is_deleted = False if material_out.parent is None: if material_out.id != material.id: material_out.parent = material material_out.cate = material.cate material_out.tracking = material_out_tracking material_out.save() return material_out material_out = Material.objects.create(**{'parent': instance.material, 'process': instance.process, 'is_hidden': True, 'name': material.name, 'number': material.number, 'specification': material.specification, 'model': material.model, 'type': Material.MA_TYPE_HALFGOOD, 'cate': material.cate, 'tracking': material_out_tracking, 'create_by': user, 'update_by': user, }) return material_out def create(self, validated_data): process = validated_data['process'] routepack = validated_data.get('routepack', None) material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH) if material_out_tracking is None: material_out_tracking = Material.MA_TRACKING_BATCH if routepack: pass # if Route.objects.filter(routepack=routepack, process=process).exists(): # raise ValidationError('已选择该工序!') else: pass # material = validated_data.get('material', None) # if material and process and Route.objects.filter(material=material, process=process).exists(): # raise ValidationError('已选择该工序!!') instance:Route = super().create(validated_data) material_out = instance.material_out if material_out: if material_out.process is None: material_out.process = process if material_out_tracking != material_out.tracking: raise ParseError("物料跟踪类型不一致!请前往物料处修改") if material_out.parent is None and instance.material: material_out.parent = instance.material material_out.save() # elif material_out.process != process: # raise ParseError('物料工序错误!请重新选择') else: if instance.material: instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.save() rx = Route.objects.filter( material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).order_by("create_time").first() if rx: instance.from_route = rx instance.save() # msg = "" # if rx.routepack: # msg = rx.routepack.name # raise ParseError(f"该工艺步骤已存在-{msg}") return instance def update(self, instance, validated_data): validated_data.pop('material', None) process = validated_data.pop('process', None) material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH) if material_out_tracking is None: material_out_tracking = Material.MA_TRACKING_BATCH instance = super().update(instance, validated_data) material_out = instance.material_out if material_out: if material_out.process is None: material_out.process = process if material_out_tracking != material_out.tracking: raise ParseError("物料跟踪类型不一致!请前往物料处修改") if material_out.parent is None and instance.material: material_out.parent = instance.material material_out.save() # elif material_out.process != process: # raise ParseError('物料工序错误!请重新选择') else: if instance.material: instance.material_out = RouteSerializer.gen_material_out(instance, material_out_tracking, user=self.request.user) instance.save() rx = Route.objects.filter( material_in=instance.material_in, material_out=instance.material_out, process=process).exclude(id=instance.id).order_by("create_time").first() if rx: instance.from_route = rx instance.save() # msg = "" # if rx.routepack: # msg = rx.routepack.name # raise ParseError(f"该工艺步骤已存在-{msg}") return instance def to_representation(self, instance): res = super().to_representation(instance) if instance.material_out: res['material_out_tracking'] = instance.material_out.tracking else: res['material_out_tracking'] = None return res class SruleSerializer(CustomModelSerializer): belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True) class Meta: model = Srule fields = '__all__' read_only_fields = EXCLUDE_FIELDS class MgroupDaysSerializer(serializers.Serializer): mgroup = serializers.CharField(label="工段ID") now = serializers.DateTimeField(label="当前时间", required=False) class RouteMatSerializer(CustomModelSerializer): material_name = serializers.StringRelatedField( source='material', read_only=True) class Meta: model = RouteMat fields = "__all__" read_only_fields = EXCLUDE_FIELDS_BASE def validate(self, attrs): route:Route = attrs["route"] if route.from_route is not None: raise ParseError("该工艺步骤引用其他步骤,无法修改") return attrs class MaterialExportSerializer(CustomModelSerializer): class Meta: model = Material fields = ["id", "number", "name", "specfication", "unit", "bin_number_main", "cate", "count_safe", "unit_price"]