441 lines
16 KiB
Python
441 lines
16 KiB
Python
from apps.inm.models import (MIO, MIOItem,
|
|
MaterialBatch, MaterialBatchA,
|
|
MIOItemA, MIOItemw)
|
|
from rest_framework.exceptions import ParseError
|
|
from apps.mtm.models import Material
|
|
from apps.mtm.services_2 import cal_material_count
|
|
from apps.wpm.models import WMaterial, BatchSt, BatchLog
|
|
from apps.wpm.services_2 import ana_batch_thread
|
|
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
|
|
|
|
# 获取defect
|
|
defect:Defect = None
|
|
if item.wm and item.mb:
|
|
raise ParseError("车间和仓库库存不能同时存在")
|
|
if item.wm:
|
|
defect = item.wm.defect
|
|
elif item.mb:
|
|
defect = item.mb.defect
|
|
|
|
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, None])
|
|
else:
|
|
action_list = [[item.material, item.batch, item.count, defect]]
|
|
|
|
if is_zhj:
|
|
try:
|
|
mb = MaterialBatch.objects.get(
|
|
material=item.material,
|
|
warehouse=item.warehouse,
|
|
batch=item.batch,
|
|
state=WMaterial.WM_OK,
|
|
defect=None
|
|
)
|
|
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]
|
|
defect:Defect = al[3]
|
|
|
|
xbatches.append(xbatch)
|
|
if xcount <= 0:
|
|
raise ParseError("存在非正数!")
|
|
mb = None
|
|
if not is_zhj:
|
|
try:
|
|
mb = MaterialBatch.objects.get(
|
|
material=xmaterial,
|
|
warehouse=item.warehouse,
|
|
batch=xbatch,
|
|
state=WMaterial.WM_OK,
|
|
defect=defect
|
|
)
|
|
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
|
raise ParseError(f"批次错误!{e}")
|
|
mb.count = mb.count - xcount
|
|
if mb.count < 0:
|
|
raise ParseError(f"{mb.batch}-批次库存不足,操作失败")
|
|
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, defect=defect)
|
|
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)
|
|
|
|
# 触发批次统计分析
|
|
ana_batch_thread(xbatches)
|
|
|
|
|
|
def do_in(item: MIOItem):
|
|
"""
|
|
生产入库后更新车间物料
|
|
"""
|
|
mio = item.mio
|
|
wmin:WMaterial = item.wm
|
|
if wmin and wmin.state != WMaterial.WM_OK:
|
|
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 # 是否组合件入仓库
|
|
|
|
# 获取defect
|
|
defect:Defect = None
|
|
if item.wm and item.mb:
|
|
raise ParseError("车间和仓库库存不能同时存在")
|
|
if item.wm:
|
|
defect = item.wm.defect
|
|
elif item.mb:
|
|
defect = item.mb.defect
|
|
|
|
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, None])
|
|
else:
|
|
action_list = [[item.material, item.batch, item.count, defect]]
|
|
|
|
production_dept = None
|
|
|
|
xbatchs = []
|
|
if is_zhj:
|
|
xbatchs = [item.batch]
|
|
for al in action_list:
|
|
xmaterial, xbatch, xcount, defect = al
|
|
if xcount <= 0:
|
|
raise ParseError("存在非正数!")
|
|
|
|
xbatchs.append(xbatch)
|
|
|
|
wm_qs = WMaterial.objects.filter(
|
|
batch=xbatch,
|
|
material=xmaterial,
|
|
belong_dept=belong_dept,
|
|
mgroup=mgroup,
|
|
defect=defect,
|
|
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=WMaterial.WM_OK,
|
|
defect=defect,
|
|
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,
|
|
defect=None,
|
|
state=WMaterial.WM_OK,
|
|
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)
|
|
|
|
# 批次统计分析
|
|
ana_batch_thread(xbatchs)
|
|
|
|
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):
|
|
"""
|
|
更新库存, 支持反向操作
|
|
"""
|
|
if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单
|
|
# 这里还需要对入厂检验进行处理
|
|
if is_reverse:
|
|
BatchLog.clear(mio=instance)
|
|
else:
|
|
for item in MIOItem.objects.filter(mio=instance):
|
|
BatchSt.g_create(
|
|
batch=item.batch, mio=instance, material_start=item.material, reuse_node=True)
|
|
from apps.pum.services import PumService
|
|
if is_reverse:
|
|
cls.update_mb(instance, -1)
|
|
else:
|
|
cls.update_mb(instance, 1)
|
|
PumService.mio_purin(instance, is_reverse)
|
|
elif instance.type == MIO.MIO_TYPE_OTHER_IN:
|
|
if is_reverse:
|
|
BatchLog.clear(mio=instance)
|
|
else:
|
|
for item in MIOItem.objects.filter(mio=instance):
|
|
BatchSt.g_create(
|
|
batch=item.batch, mio=instance, material_start=item.material, reuse_node=True)
|
|
if is_reverse:
|
|
cls.update_mb(instance, -1)
|
|
else:
|
|
cls.update_mb(instance, 1)
|
|
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:
|
|
raise ParseError("存在非正数!")
|
|
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(f"{mb.batch}-批次库存不足,操作失败")
|
|
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("不支持的操作")
|
|
|
|
# 批次统计分析
|
|
ana_batch_thread(xbatchs)
|
|
|
|
|