from apps.inm.models import (MIO, MIOItem, MaterialBatch, MaterialBatchA, MIOItemA, WareHouse, MIOItemw) from rest_framework.exceptions import ParseError from apps.mtm.models import Material, Process from apps.utils.tools import ranstr from apps.utils.thread import MyThread from apps.mtm.services_2 import cal_material_count from apps.wpm.models import WMaterial from apps.wpm.services_2 import get_alldata_with_batch_and_store from apps.wpmw.models import Wpr from apps.qm.models import Ftest, Defect from django.db.models import Count, Q def do_out(item: MIOItem): """ 生产领料到车间 """ if item.mb and item.mb.defect is not None: raise ParseError("生产领料不支持不合格品") from apps.inm.models import MaterialBatch mio:MIO = item.mio belong_dept = mio.belong_dept mgroup = mio.mgroup do_user = mio.do_user material: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() if material.tracking == Material.MA_TRACKING_SINGLE: raise ParseError("组合件暂不支持追踪单件") xbatches = [] if is_zhj: xbatches = [item.batch] for al in action_list: xmaterial:Material = al[0] xbatch:str = al[1] xcount:str = al[2] xbatches.append(xbatch) mb = None if not is_zhj: try: mb = MaterialBatch.objects.get( material=xmaterial, warehouse=item.warehouse, batch=xbatch, state=10, defect=None ) 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() # 领到车间库存(或工段) wm, new_create = WMaterial.objects.get_or_create(batch=xbatch, material=xmaterial, belong_dept=belong_dept, mgroup=mgroup, state=WMaterial.WM_OK) if new_create: wm.create_by = do_user wm.batch_ofrom = mb.batch if mb else None wm.material_ofrom = mb.material if mb else None wm.count = wm.count + item.count wm.update_by = do_user wm.save() # 开始变动wpr if xmaterial.tracking == Material.MA_TRACKING_SINGLE: mioitemws = MIOItemw.objects.filter(mioitem=item) if mioitemws.count() != item.count: raise ParseError("出入库与明细数量不一致,操作失败") for mioitemw in mioitemws: Wpr.change_or_new(wpr=mioitemw.wpr, wm=wm, old_mb=mb) # 触发批次统计分析 xbatches = list(set(xbatches)) if xbatches: for xbatch in xbatches: MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start() def do_in(item: MIOItem): """ 生产入库后更新车间物料 """ mio = item.mio if item.wm and item.wm.defect is not None: raise ParseError("不合格物料无法入库") 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 = mias.values_list('material', 'batch', 'rate') for i in mias_list: material, batch, rate = i new_count = rate * item.count # 假设 item.count 存在 action_list.append([material, batch, new_count]) else: action_list = [[item.material, item.batch, item.count]] production_dept = None xbatchs = [] if is_zhj: xbatchs = [item.batch] for al in action_list: xmaterial, xbatch, xcount = al xbatchs.append(xbatch) # 扣减车间库存 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}车间物料不足') wm_production_dept = wm.mgroup.belong_dept if wm.mgroup else wm.belong_dept if production_dept is None: production_dept = wm_production_dept elif wm_production_dept and production_dept != wm_production_dept: raise ParseError(f'{str(xmaterial)}-{xbatch}车间物料不属于同一车间') # 增加mb if not is_zhj: mb, _ = MaterialBatch.objects.get_or_create( material=xmaterial, warehouse=item.warehouse, batch=xbatch, state=10, defect=None, defaults={ "count": 0, "batch_ofrom": wm.batch_ofrom, "material_ofrom": wm.material_ofrom, "production_dept": production_dept } ) if mb.production_dept is None: mb.production_dept = production_dept mb.count = mb.count + xcount mb.save() # 开始变动wpr if xmaterial.tracking == Material.MA_TRACKING_SINGLE: mioitemws = MIOItemw.objects.filter(mioitem=item) if mioitemws.count() != item.count: raise ParseError("出入库与明细数量不一致,操作失败") for mioitemw in mioitemws: Wpr.change_or_new(wpr=mioitemw.wpr, mb=mb, old_wm=wm) if is_zhj: # 组合件单独处理并且不做追踪单个处理 mb, is_created = MaterialBatch.objects.get_or_create( material=item.material, warehouse=item.warehouse, batch=item.batch, defaults={"count": 0, "production_dept": production_dept} ) if not is_created: raise ParseError("该批次组合件已存在") if mb.production_dept is None: mb.production_dept = production_dept mb.count = mb.count + item.count mb.save() for mia in mias: MaterialBatchA.objects.create(mb=mb, material=mia.material, batch=mia.batch, rate=mia.rate) # 批次统计分析 xbatchs = list(set(xbatchs)) for xbatch in xbatchs: MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start() class InmService: @classmethod def update_material_count(cls, instance: MIO): """ 更新物料数量 """ # 统计物料数量 m_ids = list(MIOItem.objects.filter(mio=instance).values_list('material_id', flat=True)) m_ids2 = list(MIOItemA.objects.filter(mioitem__mio=instance).values_list('material_id', flat=True)) cal_material_count(m_ids+m_ids2) @classmethod def update_inm(cls, instance: MIO, is_reverse: bool = False): """ 更新库存, 支持反向操作 """ in_or_out = 1 if is_reverse: in_or_out = -1 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_OTHER_IN: cls.update_mb(instance, in_or_out) elif instance.type == MIO.MIO_TYPE_DO_IN: mioitems = MIOItem.objects.filter(mio=instance) if is_reverse: for item in mioitems: do_out(item) else: for item in mioitems: do_in(item) elif instance.type == MIO.MIO_TYPE_SALE_OUT: from apps.sam.services import SamService if is_reverse: cls.update_mb(instance, 1) else: cls.update_mb(instance, -1) SamService.mio_saleout(instance, is_reverse) elif instance.type == MIO.MIO_TYPE_OTHER_OUT: if is_reverse: cls.update_mb(instance, 1) else: cls.update_mb(instance, -1) elif instance.type == MIO.MIO_TYPE_DO_OUT: mioitems = MIOItem.objects.filter(mio=instance) if is_reverse: for item in mioitems: do_in(item) else: for item in mioitems: do_out(item) else: raise ParseError('不支持该出入库操作') @classmethod def update_mb(cls, instance: MIO, in_or_out: int = 1): """ 更新物料批次 in = 1 out = -1 """ mioitems = MIOItem.objects.filter(mio=instance) if not mioitems.exists(): raise ParseError("未填写物料明细") for i in mioitems: 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'): """ 更新物料批次(根据明细) in = 1 out = -1 默认使用count字段做加减 """ material: Material = i.material warehouse = i.warehouse tracking = material.tracking ddict = {"count": 0, "supplier": i.mio.supplier} if i.material.type in [Material.MA_TYPE_MAINSO]: ddict["batch_ofrom"] = i.batch ddict["material_ofrom"] = i.material if field == "count": count_change = i.count - i.count_notok elif field == "count_notok": count_change = getattr(i, field) else: raise ParseError("不支持该字段") m_list = [(material, warehouse, i.batch, count_change, None, i)] if tracking == Material.MA_TRACKING_SINGLE: # 获取所有主要的不合格项 mw_qs = MIOItemw.objects.filter(mioitem=i) defectIds= Ftest.objects.filter(mioitemw_ftest__in=mw_qs).exclude(defect_main=None).values_list("defect_main__id", flat=True).distinct() defects_map = {d.id: d for d in Defect.objects.filter(id__in=defectIds)} # 过滤并统计相关数据 filtered_mw_qs = mw_qs.filter( ftest__defect_main__id__in=defects_map.keys(), ).values('ftest__defect_main__id').annotate(xcount=Count('id')) for defect_data in filtered_mw_qs: defect_id = defect_data['ftest__defect_main__id'] xcount = defect_data['xcount'] if xcount > 0: defect = defects_map[defect_id] m_list.append((material, warehouse, i.batch, xcount, defect, i)) xbatchs = [] for material, warehouse, batch, change_count, defect, mioitem in m_list: xbatchs.append(batch) if change_count <= 0: continue state = WMaterial.WM_OK if defect: state = WMaterial.WM_NOTOK mb, _ = MaterialBatch.objects.get_or_create( material=material, warehouse=warehouse, batch=batch, defect=defect, state=state, defaults=ddict ) if in_or_out == 1: mb.count = mb.count + change_count mb.save() if tracking == Material.MA_TRACKING_SINGLE: if defect: mioitemws = MIOItemw.objects.filter(mioitem=i, ftest__defect_main=defect) else: mioitemws = MIOItemw.objects.filter(Q(ftest=None) | Q(ftest__defect_main=None), mioitem=i) if mioitemws.count() != change_count: raise ParseError("出入库与明细数量不一致,操作失败") for mioitemw in mioitemws: if mioitemw.wpr: Wpr.change_or_new(wpr=mioitemw.wpr, mb=mb, ftest=mioitemw.ftest) else: wpr = Wpr.change_or_new(number=mioitemw.number, mb=mb, ftest=mioitemw.ftest) mioitemw.wpr = wpr mioitemw.save() elif in_or_out == -1: mb.count = mb.count - change_count if mb.count < 0: raise ParseError("批次库存不足,操作失败") else: mb.save() if tracking == Material.MA_TRACKING_SINGLE: if defect: mioitemws = MIOItemw.objects.filter(mioitem=i, ftest__defect_main=defect) else: mioitemws = MIOItemw.objects.filter(Q(ftest=None) | Q(ftest__defect_main=None), mioitem=i) if mioitemws.count() != change_count: raise ParseError("出入库与明细数量不一致,操作失败") for mioitemw in mioitemws: Wpr.change_or_new(wpr=mioitemw.wpr, old_mb=mb) else: raise ParseError("不支持的操作") # 批次统计分析 xbatchs = list(set(xbatchs)) for xbatch in xbatchs: MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start() def daoru_mb(path: str): """ 导入物料批次(如没有物料自动创建) """ # 注释的是初次导入时做的数据矫正 # objs1 = Material.objects.filter(specification__contains=' ') # for i in objs1: # i.specification = i.specification.replace(' ', '') # i.save() # objs2 = Material.objects.filter(specification__contains='×') # for i in objs2: # i.specification = i.specification.replace('×', '*') # i.save() # objs3 = (Material.objects.filter( # specification__contains='优级') | Material.objects.filter(specification__contains='一级')).exclude(specification__contains='|') # for i in objs3: # i.specification = i.specification.replace( # '优级', '|优级').replace('一级', '|一级') # i.save() type_dict = {"主要原料": 30, "半成品": 20, "成品": 10, "辅助材料": 40, "加工工具": 50, "辅助工装": 60, "办公用品": 70} from apps.utils.snowflake import idWorker from openpyxl import load_workbook wb = load_workbook(path) process_l = Process.objects.all() process_d = {p.name: p for p in process_l} warehouse_l = WareHouse.objects.all() warehouse_d = {w.name: w for w in warehouse_l} for sheet in wb.worksheets: i = 3 while sheet[f"a{i}"].value: try: type = type_dict[sheet[f"a{i}"].value.replace(" ", "")] name = sheet[f"b{i}"].value.replace(" ", "") specification = sheet[f"c{i}"].value.replace(" ", "") if sheet[f"d{i}"].value and sheet[f"d{i}"].value.replace(" ", ""): specification = specification + "|" + sheet[f"d{i}"].value.replace(" ", "") model = sheet[f"e{i}"].value.replace(" ", "") process = process_d[sheet[f"f{i}"].value.replace(" ", "")] batch = sheet[f"g{i}"].value.replace(" ", "") count = int(sheet[f"h{i}"].value) warehouse = warehouse_d[sheet[f"i{i}"].value.replace(" ", "")] except KeyError as e: raise ParseError(f"第{i}行数据有误:{str(e)}") material, _ = Material.objects.get_or_create( type=type, name=name, specification=specification, model=model, process=process, defaults={"type": type, "name": name, "specification": specification, "model": model, "process": process, "number": ranstr(6), "id": idWorker.get_id()}, ) MaterialBatch.objects.get_or_create( material=material, batch=batch, warehouse=warehouse, defaults={"material": material, "batch": batch, "warehouse": warehouse, "count": count, "id": idWorker.get_id()} ) cal_material_count([material.id]) i = i + 1