feat: 返修逻辑完成

This commit is contained in:
caoqianming 2025-02-24 13:22:43 +08:00
parent 553fbcc826
commit 2663f20953
5 changed files with 104 additions and 62 deletions

View File

@ -8,14 +8,14 @@ class Process(CommonBModel):
工序
"""
PRO_PROD = 10
RPO_TEST = 20
PRO_TEST = 20
PRO_NORMAL = 10
PRO_DIV = 20
PRO_MERGE = 30
name = models.CharField('工序名称', max_length=100)
type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (RPO_TEST, '检验工序')))
type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序')))
mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并')))
cate = models.CharField('大类', max_length=10, default='')
sort = models.PositiveSmallIntegerField('排序', default=1)

View File

@ -107,6 +107,7 @@ class WMaterial(CommonBDModel):
WM_OK = 10
WM_NOTOK = 20
WM_REPAIR = 30
WM_REPAIRED = 34
WM_TEST = 40
WM_SCRAP = 50
state = models.PositiveSmallIntegerField('状态', default=10, choices=WmStateOption.choices)

View File

@ -486,25 +486,33 @@ class MlogInitSerializer(CustomModelSerializer):
model = Mlog
fields = ['id',
'work_start_time', 'work_end_time', 'mgroup', 'reminder_interval_list',
'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json']
'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json', 'is_fix']
extra_kwargs = {
'work_start_time': {'required': True},
'route':{'required': True},
'route':{'required': False},
'mgroup': {'required': True},
'mtype': {'required': True}
}
def validate(self, attrs):
mtype = attrs['mtype']
route: Route = attrs['route']
route: Route = attrs.get('route', None)
mgroup: Mgroup = attrs['mgroup']
if route is None:
is_fix:bool = attrs.get('is_fix', False)
if is_fix:
attrs["route"] = None
elif route is None:
raise ParseError('缺少工艺路线')
if route.process != mgroup.process:
if route and route.process != mgroup.process:
raise ParseError('工序不匹配')
attrs['hour_work'] = route.hour_work
attrs['material_in'] = route.material_in
attrs['material_out'] = route.material_out
if is_fix:
attrs['hour_work'] = None
attrs['material_in'] = None
attrs['material_out'] = None
if route:
attrs['hour_work'] = route.hour_work
attrs['material_in'] = route.material_in
attrs['material_out'] = route.material_out
attrs['fill_way'] = Mlog.MLOG_23
if mtype == Mlog.MTYPE_OUT:
supplier = attrs.get('supplier', None)
@ -535,14 +543,16 @@ class MlogbInSerializer(CustomModelSerializer):
def validate(self, attrs):
mlog: Mlog = attrs['mlog']
is_fix = mlog.is_fix
mtask: Mtask = attrs.get("mtask", None)
if mtask and mtask.state != Mtask.MTASK_ASSGINED:
raise ParseError('该任务非下达中不可选择')
wm_in: WMaterial = attrs['wm_in']
if wm_in is None:
raise ParseError("请选择相应车间库存!")
if wm_in.state in [WMaterial.WM_OK, WMaterial.WM_REPAIR]:
pass
if wm_in.state in [WMaterial.WM_OK, WMaterial.WM_REPAIR, WMaterial.WM_REPAIRED]:
if is_fix and wm_in.state not in [WMaterial.WM_REPAIR, WMaterial.WM_REPAIRED]:
raise ParseError('需要使用返修品')
else:
raise ParseError('非合格/返修品不可使用')
if mtask and mlog.route != mtask.route:
@ -552,14 +562,15 @@ class MlogbInSerializer(CustomModelSerializer):
attrs['batch'] = wm_in.batch
attrs["batch_ofrom"] = wm_in.batch_ofrom
attrs["material_ofrom"] = wm_in.material_ofrom
if route.batch_bind:
if route.batch_bind and mtask is not None:
if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists():
raise ParseError('该车间库存非本任务使用')
return attrs
def create(self, validated_data):
mlog: Mlog = validated_data['mlog']
if Mlogb.objects.filter(mlog=mlog, mtask=validated_data['mtask'], wm_in=validated_data['wm_in'], parent=None).exists():
mtask: Mtask = validated_data.get("mtask", None)
if Mlogb.objects.filter(mlog=mlog, mtask=mtask, wm_in=validated_data['wm_in'], parent=None).exists():
raise ParseError('该记录已存在')
if mlog.submit_time is not None:
raise ParseError('生产日志已提交不可编辑')
@ -567,7 +578,8 @@ class MlogbInSerializer(CustomModelSerializer):
def update(self, instance, validated_data):
mlog: Mlog = instance.mlog
if Mlogb.objects.filter(mlog=mlog, mtask=validated_data['mtask'], wm_in=validated_data['wm_in'], parent=None).exclude(id=instance.id).exists():
mtask: Mtask = instance.mtask
if Mlogb.objects.filter(mlog=mlog, mtask=mtask, wm_in=validated_data['wm_in'], parent=None).exclude(id=instance.id).exists():
raise ParseError('该记录已存在')
if mlog.submit_time is not None:
raise ParseError('生产日志已提交不可编辑')

View File

@ -22,6 +22,7 @@ from apps.wpmw.models import Wpr, WprDefect
from ..qm.models import Defect, Ftest
from django.db.models import Count, Q
from apps.utils.tasks import ctask_run
from apps.mtm.models import Process
myLogger = logging.getLogger('log')
@ -269,7 +270,13 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mo_ma, mo_batch, mo_count, mo_count_eweight, notok_sign_or_defect, mlog_or_b = mo
if mo_count <= 0:
continue
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
if mlog.is_fix:
if mlog.mgroup.process == Process.PRO_PROD:
wm_state = WMaterial.WM_REPAIRED # 返修只有返修完成品
elif mlog.mgroup.process == Process.PRO_TEST:
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
else:
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None,
'notok_sign': None, 'defect': None, 'state': wm_state}
if isinstance(notok_sign_or_defect, Defect):
@ -344,7 +351,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
material_in:Material = mlog.material_in
stored_notok = mlog.stored_notok
stored_mgroup = mlog.stored_mgroup
is_fix = mlog.is_fix
# 先回退产物
if material_out: # 产物退回
# 有多个产物的情况
@ -386,7 +393,13 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
mo_ma, mo_batch, mo_count, _, notok_sign_or_defect, mlog_or_b = mo
if mo_count == 0:
continue
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
if is_fix:
if mgroup.process.type == Process.PRO_PROD:
wm_state = WMaterial.WM_REPAIRED
else: # 检验工序正常生成
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
else:
wm_state = WMaterial.WM_OK if notok_sign_or_defect is None else WMaterial.WM_NOTOK
lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state}
if isinstance(notok_sign_or_defect, Defect):
lookup['defect'] = notok_sign_or_defect
@ -516,10 +529,11 @@ def cal_mtask_progress_from_mlog(mlog):
caled_mtask = []
for item in m_outs_qs.all():
mtask = item.mtask
if mtask in caled_mtask:
continue
update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask)
if mtask:
if mtask in caled_mtask:
continue
update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask)
def cal_mlog_count_from_mlogb(mlog: Mlog):
"""
@ -685,7 +699,12 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
)
elif handover.type == Handover.H_REPAIR:
# 返修交接
if handover.recive_mgroup:
recive_mgroup = handover.recive_mgroup
if recive_mgroup:
if recive_mgroup.process == Process.PRO_TEST:
wm_state = WMaterial.WM_REPAIRED
else:
wm_state = WMaterial.WM_REPAIR
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=material,
@ -694,7 +713,7 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
notok_sign=wm_from.notok_sign,
defect=wm_from.defect,
material_origin=material,
state=WMaterial.WM_REPAIR,
state=wm_state,
defaults={
"batch_ofrom": wm_from.batch_ofrom,
"material_ofrom": wm_from.material_ofrom,

View File

@ -541,27 +541,30 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
mlogbin: Mlogb = serializer.save()
mlog:Mlog = mlogbin.mlog
route:Route = mlog.route
mgroup:Mgroup = mlog.mgroup
is_fix = mlog.is_fix
# 以及mlogbw
if mlogbin.material_in:
material_in:Material = mlogbin.material_in
material_out:Material = mlog.material_out
if material_out is None:
raise ParseError('产物不可为空')
# 如果是主要输入物料且是主批次,才需生成输出
if route.material_in != material_in or mlogbin.parent is not None:
return
m_dict = {
"mtask": mlogbin.mtask,
"mlog": mlog,
"material_out": material_out,
}
m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog)
wm_in: WMaterial = mlogbin.wm_in
mlogbout, is_create = Mlogb.objects.get_or_create(**m_dict, defaults=
{"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom})
if is_create and route:
d_count_real = 0
d_count_ok = 0
material_in:Material = mlogbin.material_in
# 如果是返修,则输出和输入相同
material_out:Material = mlog.material_out if is_fix is False else material_in
if material_out is None:
raise ParseError('产物不可为空')
# 如果是主要输入物料且是主批次,才需生成输出
if route and route.material_in != material_in or mlogbin.parent is not None:
return
m_dict = {
"mtask": mlogbin.mtask,
"mlog": mlog,
"material_out": material_out,
}
m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog)
wm_in: WMaterial = mlogbin.wm_in
mlogbout, is_create = Mlogb.objects.get_or_create(**m_dict, defaults=
{"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom})
if is_create:
d_count_real = 0
d_count_ok = 0
if route:
if route.process.mtype == Process.PRO_NORMAL:
d_count_real = mlogbin.count_use
d_count_ok = mlogbin.count_use
@ -573,26 +576,33 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
xcount = math.floor( mlogbin.count_use / route.div_number)
d_count_real = xcount
d_count_ok = xcount
# 找寻质检表
if material_out.tracking == Material.MA_TRACKING_SINGLE:
elif is_fix:
d_count_real = mlogbin.count_use
d_count_ok = mlogbin.count_use
# 找寻质检表
if material_out.tracking == Material.MA_TRACKING_SINGLE:
if is_fix and mgroup.process.type == Process.PRO_PROD:
# 如果是生产返修,则忽略质检
pass
else:
qctmat = QctMat.objects.filter(material=material_out, qct__is_deleted=False).order_by("-create_time").first()
mlogbout.qct = qctmat.qct if qctmat else None
mlogbout.count_real = d_count_real
mlogbout.count_ok = d_count_ok
mlogbout.save()
mlogbin.mlogb_to = mlogbout
mlogbin.save()
if material_in.tracking == Material.MA_TRACKING_SINGLE:
m_out_t = material_out.tracking
if mlogbin.count_use == wm_in.count: # 自动创建mlogbw
for wpr in Wpr.objects.filter(wm=wm_in).order_by("number"):
Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbin, defaults={"number": wpr.number})
if m_out_t == Material.MA_TRACKING_SINGLE:
if route.process.mtype == Process.PRO_NORMAL:
Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbout, defaults={"number": wpr.number})
elif route.process.mtype == Process.PRO_DIV:
for i in range(route.div_number):
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr.number}-{i+1}')
mlogbout.count_real = d_count_real
mlogbout.count_ok = d_count_ok
mlogbout.save()
mlogbin.mlogb_to = mlogbout
mlogbin.save()
if material_in.tracking == Material.MA_TRACKING_SINGLE:
m_out_t = material_out.tracking
if mlogbin.count_use == wm_in.count: # 自动创建mlogbw
for wpr in Wpr.objects.filter(wm=wm_in).order_by("number"):
Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbin, defaults={"number": wpr.number})
if m_out_t == Material.MA_TRACKING_SINGLE:
if route.process.mtype == Process.PRO_NORMAL:
Mlogbw.objects.get_or_create(wpr=wpr, mlogb=mlogbout, defaults={"number": wpr.number})
elif route.process.mtype == Process.PRO_DIV:
for i in range(route.div_number):
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f'{wpr.number}-{i+1}')
class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):