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 PRO_PROD = 10
RPO_TEST = 20 PRO_TEST = 20
PRO_NORMAL = 10 PRO_NORMAL = 10
PRO_DIV = 20 PRO_DIV = 20
PRO_MERGE = 30 PRO_MERGE = 30
name = models.CharField('工序名称', max_length=100) 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, '合并'))) mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并')))
cate = models.CharField('大类', max_length=10, default='') cate = models.CharField('大类', max_length=10, default='')
sort = models.PositiveSmallIntegerField('排序', default=1) sort = models.PositiveSmallIntegerField('排序', default=1)

View File

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

View File

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

View File

@ -22,6 +22,7 @@ from apps.wpmw.models import Wpr, WprDefect
from ..qm.models import Defect, Ftest from ..qm.models import Defect, Ftest
from django.db.models import Count, Q from django.db.models import Count, Q
from apps.utils.tasks import ctask_run from apps.utils.tasks import ctask_run
from apps.mtm.models import Process
myLogger = logging.getLogger('log') 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 mo_ma, mo_batch, mo_count, mo_count_eweight, notok_sign_or_defect, mlog_or_b = mo
if mo_count <= 0: if mo_count <= 0:
continue 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, lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None,
'notok_sign': None, 'defect': None, 'state': wm_state} 'notok_sign': None, 'defect': None, 'state': wm_state}
if isinstance(notok_sign_or_defect, Defect): 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 material_in:Material = mlog.material_in
stored_notok = mlog.stored_notok stored_notok = mlog.stored_notok
stored_mgroup = mlog.stored_mgroup stored_mgroup = mlog.stored_mgroup
is_fix = mlog.is_fix
# 先回退产物 # 先回退产物
if material_out: # 产物退回 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 mo_ma, mo_batch, mo_count, _, notok_sign_or_defect, mlog_or_b = mo
if mo_count == 0: if mo_count == 0:
continue 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} lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': None, 'defect': None, 'state': wm_state}
if isinstance(notok_sign_or_defect, Defect): if isinstance(notok_sign_or_defect, Defect):
lookup['defect'] = notok_sign_or_defect lookup['defect'] = notok_sign_or_defect
@ -516,10 +529,11 @@ def cal_mtask_progress_from_mlog(mlog):
caled_mtask = [] caled_mtask = []
for item in m_outs_qs.all(): for item in m_outs_qs.all():
mtask = item.mtask mtask = item.mtask
if mtask in caled_mtask: if mtask:
continue if mtask in caled_mtask:
update_mtask(mtask, fill_way=mlog.fill_way) continue
caled_mtask.append(mtask) update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask)
def cal_mlog_count_from_mlogb(mlog: Mlog): 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: 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( wm_to, _ = WMaterial.objects.get_or_create(
batch=batch, batch=batch,
material=material, material=material,
@ -694,7 +713,7 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
notok_sign=wm_from.notok_sign, notok_sign=wm_from.notok_sign,
defect=wm_from.defect, defect=wm_from.defect,
material_origin=material, material_origin=material,
state=WMaterial.WM_REPAIR, state=wm_state,
defaults={ defaults={
"batch_ofrom": wm_from.batch_ofrom, "batch_ofrom": wm_from.batch_ofrom,
"material_ofrom": wm_from.material_ofrom, "material_ofrom": wm_from.material_ofrom,

View File

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