feat: MaterialBatch 添加字段以支持追踪原料批次4

This commit is contained in:
caoqianming 2024-11-14 10:40:14 +08:00
parent b9d9cf42cb
commit b5928b2927
3 changed files with 181 additions and 171 deletions

View File

@ -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:

View File

@ -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

View File

@ -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]):
"""
生产日志提交后需要执行的操作