diff --git a/apps/mtm/models.py b/apps/mtm/models.py index 60415aa5..793ef03e 100644 --- a/apps/mtm/models.py +++ b/apps/mtm/models.py @@ -8,14 +8,14 @@ class Process(CommonBModel): 工序 """ PRO_PROD = 10 - RPO_TEST = 20 + PRO_TEST = 20 PRO_NORMAL = 10 PRO_DIV = 20 PRO_MERGE = 30 name = models.CharField('工序名称', max_length=100) - type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (RPO_TEST, '检验工序'))) + type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序'))) mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并'))) cate = models.CharField('大类', max_length=10, default='') sort = models.PositiveSmallIntegerField('排序', default=1) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 5ba603bb..3b1a9745 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -107,6 +107,7 @@ class WMaterial(CommonBDModel): WM_OK = 10 WM_NOTOK = 20 WM_REPAIR = 30 + WM_REPAIRED = 34 WM_TEST = 40 WM_SCRAP = 50 state = models.PositiveSmallIntegerField('状态', default=10, choices=WmStateOption.choices) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 6c88d994..2132be70 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -486,25 +486,33 @@ class MlogInitSerializer(CustomModelSerializer): model = Mlog fields = ['id', 'work_start_time', 'work_end_time', 'mgroup', 'reminder_interval_list', - 'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json'] + 'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json', 'is_fix'] extra_kwargs = { 'work_start_time': {'required': True}, - 'route':{'required': True}, + 'route':{'required': False}, 'mgroup': {'required': True}, 'mtype': {'required': True} } def validate(self, attrs): mtype = attrs['mtype'] - route: Route = attrs['route'] + route: Route = attrs.get('route', None) mgroup: Mgroup = attrs['mgroup'] - if route is None: + is_fix:bool = attrs.get('is_fix', False) + if is_fix: + attrs["route"] = None + elif route is None: raise ParseError('缺少工艺路线') - if route.process != mgroup.process: + 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 + if is_fix: + attrs['hour_work'] = None + attrs['material_in'] = None + attrs['material_out'] = None + if route: + 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 mtype == Mlog.MTYPE_OUT: supplier = attrs.get('supplier', None) @@ -535,14 +543,16 @@ class MlogbInSerializer(CustomModelSerializer): def validate(self, attrs): 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 wm_in.state in [WMaterial.WM_OK, WMaterial.WM_REPAIR]: - pass + if wm_in.state in [WMaterial.WM_OK, WMaterial.WM_REPAIR, WMaterial.WM_REPAIRED]: + if is_fix and wm_in.state not in [WMaterial.WM_REPAIR, WMaterial.WM_REPAIRED]: + raise ParseError('需要使用返修品') else: raise ParseError('非合格/返修品不可使用') if mtask and mlog.route != mtask.route: @@ -552,14 +562,15 @@ class MlogbInSerializer(CustomModelSerializer): attrs['batch'] = wm_in.batch attrs["batch_ofrom"] = wm_in.batch_ofrom attrs["material_ofrom"] = wm_in.material_ofrom - if route.batch_bind: + if 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'] - if Mlogb.objects.filter(mlog=mlog, mtask=validated_data['mtask'], wm_in=validated_data['wm_in'], parent=None).exists(): + mtask: Mtask = validated_data.get("mtask", None) + 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('生产日志已提交不可编辑') @@ -567,7 +578,8 @@ class MlogbInSerializer(CustomModelSerializer): def update(self, instance, validated_data): mlog: Mlog = instance.mlog - if Mlogb.objects.filter(mlog=mlog, mtask=validated_data['mtask'], wm_in=validated_data['wm_in'], parent=None).exclude(id=instance.id).exists(): + mtask: Mtask = instance.mtask + if Mlogb.objects.filter(mlog=mlog, mtask=mtask, wm_in=validated_data['wm_in'], parent=None).exclude(id=instance.id).exists(): raise ParseError('该记录已存在') if mlog.submit_time is not None: raise ParseError('生产日志已提交不可编辑') diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 5ca47106..9498971d 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -22,6 +22,7 @@ from apps.wpmw.models import Wpr, WprDefect from ..qm.models import Defect, Ftest from django.db.models import Count, Q from apps.utils.tasks import ctask_run +from apps.mtm.models import Process myLogger = logging.getLogger('log') @@ -269,7 +270,13 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mo_ma, mo_batch, mo_count, mo_count_eweight, notok_sign_or_defect, mlog_or_b = mo if mo_count <= 0: continue - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + if mlog.is_fix: + if mlog.mgroup.process == Process.PRO_PROD: + wm_state = WMaterial.WM_REPAIRED # 返修只有返修完成品 + elif mlog.mgroup.process == Process.PRO_TEST: + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + else: + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state} if isinstance(notok_sign_or_defect, Defect): @@ -344,7 +351,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): material_in:Material = mlog.material_in stored_notok = mlog.stored_notok stored_mgroup = mlog.stored_mgroup - + is_fix = mlog.is_fix # 先回退产物 if material_out: # 产物退回 # 有多个产物的情况 @@ -386,7 +393,13 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mo_ma, mo_batch, mo_count, _, notok_sign_or_defect, mlog_or_b = mo if mo_count == 0: continue - wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + if is_fix: + if mgroup.process.type == Process.PRO_PROD: + wm_state = WMaterial.WM_REPAIRED + else: # 检验工序正常生成 + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK + else: + wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state} if isinstance(notok_sign_or_defect, Defect): lookup['defect'] = notok_sign_or_defect @@ -516,10 +529,11 @@ def cal_mtask_progress_from_mlog(mlog): caled_mtask = [] for item in m_outs_qs.all(): mtask = item.mtask - if mtask in caled_mtask: - continue - update_mtask(mtask, fill_way=mlog.fill_way) - caled_mtask.append(mtask) + if mtask: + if mtask in caled_mtask: + continue + update_mtask(mtask, fill_way=mlog.fill_way) + caled_mtask.append(mtask) def cal_mlog_count_from_mlogb(mlog: Mlog): """ @@ -685,7 +699,12 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, ) elif handover.type == Handover.H_REPAIR: # 返修交接 - if handover.recive_mgroup: + recive_mgroup = handover.recive_mgroup + if recive_mgroup: + if recive_mgroup.process == Process.PRO_TEST: + wm_state = WMaterial.WM_REPAIRED + else: + wm_state = WMaterial.WM_REPAIR wm_to, _ = WMaterial.objects.get_or_create( batch=batch, material=material, @@ -694,7 +713,7 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, notok_sign=wm_from.notok_sign, defect=wm_from.defect, material_origin=material, - state=WMaterial.WM_REPAIR, + state=wm_state, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 4305ff23..0d835149 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -541,27 +541,30 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust mlogbin: Mlogb = serializer.save() mlog:Mlog = mlogbin.mlog route:Route = mlog.route + mgroup:Mgroup = mlog.mgroup + is_fix = mlog.is_fix # 以及mlogbw - if mlogbin.material_in: - material_in:Material = mlogbin.material_in - material_out:Material = mlog.material_out - if material_out is None: - raise ParseError('产物不可为空') - # 如果是主要输入物料且是主批次,才需生成输出 - if route.material_in != material_in or mlogbin.parent is not None: - return - m_dict = { - "mtask": mlogbin.mtask, - "mlog": mlog, - "material_out": material_out, - } - m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog) - wm_in: WMaterial = mlogbin.wm_in - mlogbout, is_create = Mlogb.objects.get_or_create(**m_dict, defaults= - {"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom}) - if is_create and route: - d_count_real = 0 - d_count_ok = 0 + material_in:Material = mlogbin.material_in + # 如果是返修,则输出和输入相同 + material_out:Material = mlog.material_out if is_fix is False else material_in + if material_out is None: + raise ParseError('产物不可为空') + # 如果是主要输入物料且是主批次,才需生成输出 + if route and route.material_in != material_in or mlogbin.parent is not None: + return + m_dict = { + "mtask": mlogbin.mtask, + "mlog": mlog, + "material_out": material_out, + } + m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog) + wm_in: WMaterial = mlogbin.wm_in + mlogbout, is_create = Mlogb.objects.get_or_create(**m_dict, defaults= + {"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom}) + if is_create: + d_count_real = 0 + d_count_ok = 0 + if route: if route.process.mtype == Process.PRO_NORMAL: d_count_real = mlogbin.count_use d_count_ok = mlogbin.count_use @@ -573,26 +576,33 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust xcount = math.floor( mlogbin.count_use / route.div_number) d_count_real = xcount d_count_ok = xcount - # 找寻质检表 - if material_out.tracking == Material.MA_TRACKING_SINGLE: + elif is_fix: + d_count_real = mlogbin.count_use + d_count_ok = mlogbin.count_use + # 找寻质检表 + if material_out.tracking == Material.MA_TRACKING_SINGLE: + if is_fix and mgroup.process.type == Process.PRO_PROD: + # 如果是生产返修,则忽略质检 + pass + else: qctmat = QctMat.objects.filter(material=material_out, qct__is_deleted=False).order_by("-create_time").first() mlogbout.qct = qctmat.qct if qctmat else None - mlogbout.count_real = d_count_real - mlogbout.count_ok = d_count_ok - mlogbout.save() - mlogbin.mlogb_to = mlogbout - mlogbin.save() - if material_in.tracking == Material.MA_TRACKING_SINGLE: - m_out_t = material_out.tracking - if mlogbin.count_use == wm_in.count: # 自动创建mlogbw - for wpr in Wpr.objects.filter(wm=wm_in).order_by("number"): - Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbin, defaults={"number": wpr.number}) - if m_out_t == Material.MA_TRACKING_SINGLE: - if route.process.mtype == Process.PRO_NORMAL: - Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbout, defaults={"number": wpr.number}) - elif route.process.mtype == Process.PRO_DIV: - for i in range(route.div_number): - Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr.number}-{i+1}') + mlogbout.count_real = d_count_real + mlogbout.count_ok = d_count_ok + mlogbout.save() + mlogbin.mlogb_to = mlogbout + mlogbin.save() + if material_in.tracking == Material.MA_TRACKING_SINGLE: + m_out_t = material_out.tracking + if mlogbin.count_use == wm_in.count: # 自动创建mlogbw + for wpr in Wpr.objects.filter(wm=wm_in).order_by("number"): + Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbin, defaults={"number": wpr.number}) + if m_out_t == Material.MA_TRACKING_SINGLE: + if route.process.mtype == Process.PRO_NORMAL: + Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbout, defaults={"number": wpr.number}) + elif route.process.mtype == Process.PRO_DIV: + for i in range(route.div_number): + Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr.number}-{i+1}') class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):