factory/apps/inm/services.py

444 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = []
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 = []
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 None
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))
for material, warehouse, batch, change_count, defect, mioitem in m_list:
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("不支持的操作")
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