From b5928b2927866e7252c8377f751e5d1685dee170 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 14 Nov 2024 10:40:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20MaterialBatch=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BB=A5=E6=94=AF=E6=8C=81=E8=BF=BD=E8=B8=AA?= =?UTF-8?q?=E5=8E=9F=E6=96=99=E6=89=B9=E6=AC=A14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 228 +++++++++++++++++++++++++++++++++---------- apps/inm/views.py | 5 +- apps/wpm/services.py | 119 ---------------------- 3 files changed, 181 insertions(+), 171 deletions(-) diff --git a/apps/inm/services.py b/apps/inm/services.py index c4d756a4..f01a31d8 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -1,14 +1,169 @@ from apps.inm.models import MIO, MIOItem, MaterialBatch, MaterialBatchA, MIOItemA, WareHouse -from rest_framework.exceptions import ValidationError, ParseError -from django.db.models.aggregates import Sum -from django.db.models import F -from apps.wpm.services import do_out, do_in +from rest_framework.exceptions import ParseError from apps.mtm.models import Material, Process from apps.utils.tools import ranstr -from apps.system.models import Dept from apps.utils.thread import MyThread from apps.mtm.services import cal_material_count +from apps.wpm.models import WMaterial +def do_out(item: MIOItem): + """ + 生产领料到车间 + """ + from apps.inm.models import MaterialBatch + mio:MIO = item.mio + belong_dept = mio.belong_dept + mgroup = mio.mgroup + do_user = mio.do_user + material = item.material + if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目 + return + action_list = [] + mias = MIOItemA.objects.filter(mioitem=item) + is_zhj = False # 是否组合件领料 + if mias.exists(): + is_zhj = True + mias_list = list(mias.values_list('material', 'batch', 'rate')) + for i in range(len(mias_list)): + material, batch, rate = mias_list[i] + new_count = rate * item.count # 假设 item.count 存在 + action_list.append([material, batch, new_count]) + else: + action_list = [[item.material, item.batch, item.count]] + + if is_zhj: + try: + mb = MaterialBatch.objects.get( + material=item.material, + warehouse=item.warehouse, + batch=item.batch + ) + except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e: + raise ParseError(f"组合件批次错误!{e}") + mb.count = mb.count - item.count + if mb.count < 0: + raise ParseError("组合件批次库存不足,操作失败") + else: + mb.save() + + for al in action_list: + xmaterial:Material = al[0] + xbatch:str = al[1] + xcount:str = al[2] + + mb = None + if not is_zhj: + try: + mb = MaterialBatch.objects.get( + material=xmaterial, + warehouse=item.warehouse, + batch=xbatch + ) + except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e: + raise ParseError(f"批次错误!{e}") + mb.count = mb.count - xcount + if mb.count < 0: + raise ParseError("批次库存不足,操作失败") + else: + mb.save() + + # 领到车间库存(或工段) + ddict = { + "batch": xbatch, + "material": xmaterial, + "count": xcount, + "create_by": do_user, + "belong_dept": belong_dept, + "mgroup": mgroup, + "state": WMaterial.WM_OK, + } + wm, new_create = WMaterial.objects.get_or_create(batch=xbatch, material=xmaterial, + belong_dept=belong_dept, mgroup=mgroup, + state=WMaterial.WM_OK, + defaults=ddict) + if not new_create: + wm.count = wm.count + item.count + wm.update_by = do_user + wm.batch_ofrom = mb.batch_ofrom if mb else None + wm.material_ofrom = mb.material_ofrom if mb else None + wm.save() + + +def do_in(item: MIOItem): + """ + 生产入库后更新车间物料 + """ + mio = item.mio + belong_dept = mio.belong_dept + mgroup = mio.mgroup + do_user = mio.do_user + material = item.material + if material.into_wm is False: + return + action_list = [] + mias = MIOItemA.objects.filter(mioitem=item) + is_zhj = False # 是否组合件入仓库 + if mias.exists(): + is_zhj = True + mias_list = list(mias.values_list('material', 'batch', 'rate')) + for i in range(len(mias_list)): + material, batch, rate = mias_list[i] + new_count = rate * item.count # 假设 item.count 存在 + action_list.append([material, batch, new_count]) + else: + action_list = [[item.material, item.batch, item.count]] + + for al in action_list: + xmaterial, xbatch, xcount = al + + # 扣减车间库存 + wm_qs = WMaterial.objects.filter( + batch=xbatch, + material=xmaterial, + belong_dept=belong_dept, + mgroup=mgroup, + state=WMaterial.WM_OK) + count_x = wm_qs.count() + if count_x == 1: + wm = wm_qs.first() + elif count_x == 0: + raise ParseError( + f'{str(xmaterial)}-{xbatch}-批次库存不存在!') + else: + raise ParseError( + f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') + new_count = wm.count - xcount + if new_count >= 0: + wm.count = new_count + wm.update_by = do_user + wm.save() + else: + raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足') + + # 增加mb + if not is_zhj: + mb, _ = MaterialBatch.objects.get( + material=xmaterial, + warehouse=item.warehouse, + batch=xbatch, + defaults={"count": 0, "batch_ofrom": wm.batch_ofrom, "material_ofrom": xmaterial} + ) + mb.count = mb.count + xcount + mb.save() + + + if is_zhj: # 组合件单独处理 + mb, is_created = MaterialBatch.objects.get( + material=item.material, + warehouse=item.warehouse, + batch=item.batch, + defaults={"count": 0} + ) + if not is_created: + raise ParseError("该批次组合件已存在") + for mia in mias: + MaterialBatchA.objects.create(mb=mb, material=mia.material, batch=mia.batch, rate=mia.rate) + class InmService: @classmethod @@ -29,30 +184,35 @@ class InmService: in_or_out = 1 if is_reverse: in_or_out = -1 - cls.update_mb(instance, in_or_out) + if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单 from apps.pum.services import PumService - + cls.update_mb(instance, in_or_out) PumService.mio_purin(instance, is_reverse) elif instance.type == MIO.MIO_TYPE_DO_IN: + mioitems = MIO.objects.filter(mio=instance) if is_reverse: - do_out(instance) + for item in mioitems: + do_out(item) else: - do_in(instance) + for item in mioitems: + do_in(item) elif instance.type in [MIO.MIO_TYPE_DO_OUT, MIO.MIO_TYPE_SALE_OUT, MIO.MIO_TYPE_OTHER_OUT]: # 生产领料 销售出库 in_or_out = -1 if is_reverse: in_or_out = 1 - cls.update_mb(instance, in_or_out) if instance.type == MIO.MIO_TYPE_SALE_OUT: from apps.sam.services import SamService - + cls.update_mb(instance, in_or_out) SamService.mio_saleout(instance, is_reverse) elif instance.type == MIO.MIO_TYPE_DO_OUT: + mioitems = MIO.objects.filter(mio=instance) if is_reverse: - do_in(instance) + for item in mioitems: + do_in(item) else: - do_out(instance) + for item in mioitems: + do_out(item) else: raise ParseError('不支持该出入库操作') @@ -67,64 +227,34 @@ class InmService: mioitems = MIOItem.objects.filter(mio=instance) if not mioitems.exists(): raise ParseError("未填写物料明细") - type = instance.type - belong_dept = instance.belong_dept for i in mioitems: - cls.update_mb_item(i, in_or_out, 'count', type, belong_dept) + cls.update_mb_item(i, in_or_out, 'count') @classmethod - def update_mb_item(cls, i: MIOItem, in_or_out: int = 1, field:str='count', type: str =None, belong_dept: Dept=None): + def update_mb_item(cls, i: MIOItem, in_or_out: int = 1, field:str='count'): """ 更新物料批次(根据明细) in = 1 out = -1 默认使用count字段做加减 """ - mio = i.mio - if type is None or belong_dept is None: - type = mio.type - belong_dept = mio.belong_dept material = i.material warehouse = i.warehouse ddict = {"count": 0, "supplier": i.mio.supplier} - if material.type == Material.MA_TYPE_MAINSO: + if i.material.type in [Material.MA_TYPE_MAINSO]: ddict["batch_ofrom"] = i.batch - ddict["material_ofrom"] = material - mb, is_created = MaterialBatch.objects.get_or_create( + ddict["material_ofrom"] = i.material + mb, _ = MaterialBatch.objects.get_or_create( material=material, warehouse=warehouse, batch=i.batch, defaults=ddict ) - if (mio.type in [MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_OTHER_IN] and - mb.batch_ofrom is None and - material.type in [Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]): - from apps.wpm.models import WMaterial - wm_qs = WMaterial.objects.filter(material=material, batch=i.batch) - batchs_count = wm_qs.exclude(batch_ofrom=None).values_list('batch_ofrom', flat=True).distinct().count() - if batchs_count > 1: - raise ParseError("半成品/成品同批次号的原料批次应相同!") - elif batchs_count == 1: - wm = wm_qs.first() - mb.batch_ofrom = wm.batch_ofrom - mb.material_ofrom = wm.material_ofrom - mb.save() - + if in_or_out == 1: mb.count = mb.count + getattr(i, field) - if type == MIO.MIO_TYPE_DO_IN: # 生产入库需要记录production_dept字段 - if mb.production_dept is None or mb.production_dept == belong_dept: - mb.production_dept = belong_dept - else: - raise ParseError("同种物料不同生产车间应该有不同批次号!") mb.save() - mias = MIOItemA.objects.filter(mioitem=i) - if mias.exists(): # 组合件入库 - if not is_created: - raise ParseError("该批次组合件已存在") - for mia in mias: - MaterialBatchA.objects.create(mb=mb, material=mia.material, batch=mia.batch) elif in_or_out == -1: mb.count = mb.count - getattr(i, field) if mb.count < 0: diff --git a/apps/inm/views.py b/apps/inm/views.py index 4c6cdbc8..451407d5 100644 --- a/apps/inm/views.py +++ b/apps/inm/views.py @@ -257,7 +257,7 @@ class MIOItemViewSet(ListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin sr.is_valid(raise_exception=True) sr.save() # 开始变动库存 - InmService.update_mb_item(ins, -1, 'count_notok', mio.type, mio.belong_dept) + InmService.update_mb_item(ins, -1, 'count_notok') return Response() @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer) @@ -267,11 +267,10 @@ class MIOItemViewSet(ListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin 检验撤回 """ ins: MIOItem = self.get_object() - mio: MIO = ins.mio if ins.test_date is None: raise ParseError('该明细还未检验') if ins.count_notok > 0: - InmService.update_mb_item(ins, 1, 'count_notok', mio.type, mio.belong_dept) + InmService.update_mb_item(ins, 1, 'count_notok') elif ins.count_notok == 0: pass ins.test_date = None diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 51da55d2..6383dd92 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -8,14 +8,12 @@ from typing import Union from rest_framework.exceptions import ParseError from apps.system.models import User -from apps.inm.models import MIO, MIOItem, MIOItemA, MaterialBatch from apps.pm.models import Mtask from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack from .models import SfLog, WMaterial, Mlog, Mlogb, Handover, Handoverb from apps.mtm.services import cal_material_count from apps.wf.models import Ticket -from django.db import transaction from apps.utils.thread import MyThread import logging myLogger = logging.getLogger('log') @@ -112,123 +110,6 @@ def get_pcoal_heat(year_s: int, month_s: int, day_s: int): return 6000 -def do_out(mio: MIO): - """ - 生产领料到车间 - """ - belong_dept = mio.belong_dept - mgroup = mio.mgroup - do_user = mio.do_user - mioitems = MIOItem.objects.filter(mio=mio) - for item in mioitems: - material = item.material - if material.into_wm is False: - continue - # 用于混料的原料不与车间库存交互 - # if material.type in [Material.MA_TYPE_MAINSO, Material.MA_TYPE_HELPSO]: # hard code - # continue - action_list = [] - mias = MIOItemA.objects.filter(mioitem=item) - if mias.exists(): - mias_list = list(mias.values_list('material', 'batch', 'rate')) - for i in range(len(mias_list)): - material, batch, rate = mias_list[i] - new_count = rate * item.count # 假设 item.count 存在 - action_list.append([material, batch, new_count]) - else: - action_list = [[item.material, item.batch, item.count]] - for al in action_list: - xmaterial, xbatch, xcount = al - ddict = { - "batch": xbatch, - "material": xmaterial, - "count": xcount, - "create_by": do_user, - "belong_dept": belong_dept, - "mgroup": mgroup, - "state": WMaterial.WM_OK, - } - if xmaterial.type == Material.MA_TYPE_MAINSO: - ddict["batch_ofrom"] = xbatch - ddict["material_ofrom"] = xmaterial - # 领到车间库存(或工段) - wm, new_create = WMaterial.objects.get_or_create(batch=xbatch, material=xmaterial, - belong_dept=belong_dept, mgroup=mgroup, - state=WMaterial.WM_OK, - defaults=ddict) - if not new_create: - wm.count = wm.count + item.count - wm.update_by = do_user - wm.save() - if wm.batch_ofrom is None and wm.material.type in [Material.MA_TYPE_HALFGOOD, Material.MA_TYPE_GOOD]: - from apps.inm.models import MaterialBatch - mb_qs = MaterialBatch.objects.filter(material=wm.material, batch=wm.batch) - batchs_count = mb_qs.exclude(batch_ofrom=None).values_list('batch_ofrom', flat=True).distinct().count() - if batchs_count > 1: - raise ParseError("半成品/成品同批次号的原料批次应相同!") - elif batchs_count == 1: - mb = mb_qs.first() - wm.batch_ofrom = mb.batch_ofrom - wm.material_ofrom = mb.material_ofrom - wm.save() - - -def do_in(mio: MIO): - """ - 生产入库后更新车间物料 - """ - belong_dept = mio.belong_dept - mgroup = mio.mgroup - do_user = mio.do_user - mioitems = MIOItem.objects.filter(mio=mio) - for item in mioitems: - material = item.material - if material.into_wm is False: - continue - # 用于混料的原料不与车间库存交互 - # if material.type in [Material.MA_TYPE_MAINSO, Material.MA_TYPE_HELPSO]: # hard code - # continue - action_list = [] - mias = MIOItemA.objects.filter(mioitem=item) - if mias.exists(): # 组合件入库 - mias_list = list(mias.values_list('material', 'batch', 'rate')) - for i in range(len(mias_list)): - material, batch, rate = mias_list[i] - new_count = rate * item.count # 假设 item.count 存在 - action_list.append([material, batch, new_count]) - else: - action_list = [[item.material, item.batch, item.count]] - for al in action_list: - xmaterial, xbatch, xcount = al - # 优先从车间库存里拿 - wm_qs = WMaterial.objects.filter( - batch=xbatch, - material=xmaterial, - belong_dept=belong_dept, - mgroup=mgroup, - state=WMaterial.WM_OK) - # if not wm_qs.exists(): - # wm_qs = WMaterial.objects.filter(batch=xbatch, material=xmaterial, belong_dept=belong_dept, mgroup__isnull=False, notok_sign=None, material_origin=None, count_xtest=None) - - count_x = wm_qs.count() - if count_x == 1: - wm = wm_qs.first() - elif count_x == 0: - raise ParseError( - f'{str(xmaterial)}-{xbatch}-批次库存不存在!') - else: - raise ParseError( - f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') - - new_count = wm.count - xcount - if new_count >= 0: - wm.count = new_count - wm.update_by = do_user - wm.save() - else: - raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不足') - - def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): """ 生产日志提交后需要执行的操作