feat: 单日志支持多工艺步骤
This commit is contained in:
parent
a3b0455fb4
commit
620e547da5
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 3.2.12 on 2025-06-30 08:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mtm', '0059_material_bin_number_main'),
|
||||||
|
('wpm', '0118_batchst_mioitem'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='mlogb',
|
||||||
|
name='route',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.route', verbose_name='生产步骤'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -368,7 +368,7 @@ class Mlogb(BaseModel):
|
||||||
batch = models.TextField('批次号', null=True, blank=True, db_index=True)
|
batch = models.TextField('批次号', null=True, blank=True, db_index=True)
|
||||||
mtask = models.ForeignKey(Mtask, verbose_name='关联任务',
|
mtask = models.ForeignKey(Mtask, verbose_name='关联任务',
|
||||||
on_delete=models.CASCADE, related_name='mlogb_mtask', null=True, blank=True)
|
on_delete=models.CASCADE, related_name='mlogb_mtask', null=True, blank=True)
|
||||||
|
route = models.ForeignKey(Route, verbose_name='生产步骤', on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
wm_in = models.ForeignKey(WMaterial, verbose_name='投入物料所在库存',
|
wm_in = models.ForeignKey(WMaterial, verbose_name='投入物料所在库存',
|
||||||
on_delete=models.SET_NULL, null=True, blank=True)
|
on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
material_in = models.ForeignKey(
|
material_in = models.ForeignKey(
|
||||||
|
|
|
@ -378,6 +378,7 @@ class MlogSerializer(CustomModelSerializer):
|
||||||
add_dict = {
|
add_dict = {
|
||||||
'mlog': instance, 'batch': batch_in, 'wm_in': wm_in,
|
'mlog': instance, 'batch': batch_in, 'wm_in': wm_in,
|
||||||
'mtask': instance.mtask, 'material_in': instance.material_in,
|
'mtask': instance.mtask, 'material_in': instance.material_in,
|
||||||
|
'route': instance.route,
|
||||||
'count_use': instance.count_use, 'count_break': instance.count_break,
|
'count_use': instance.count_use, 'count_break': instance.count_break,
|
||||||
'count_pn_jgqbl': instance.count_pn_jgqbl
|
'count_pn_jgqbl': instance.count_pn_jgqbl
|
||||||
}
|
}
|
||||||
|
@ -403,6 +404,7 @@ class MlogSerializer(CustomModelSerializer):
|
||||||
mlogbx = Mlogb.objects.create(
|
mlogbx = Mlogb.objects.create(
|
||||||
mlog=instance,
|
mlog=instance,
|
||||||
batch=instance.batch,
|
batch=instance.batch,
|
||||||
|
route=instance.route,
|
||||||
mtask=instance.mtask,
|
mtask=instance.mtask,
|
||||||
material_out=item['material_out'],
|
material_out=item['material_out'],
|
||||||
count_ok=item['count_ok'])
|
count_ok=item['count_ok'])
|
||||||
|
@ -423,7 +425,7 @@ class MlogSerializer(CustomModelSerializer):
|
||||||
add_dict_2 = {
|
add_dict_2 = {
|
||||||
'mlog': instance, 'batch': batch_out,
|
'mlog': instance, 'batch': batch_out,
|
||||||
'mtask': instance.mtask, 'material_out': instance.material_out,
|
'mtask': instance.mtask, 'material_out': instance.material_out,
|
||||||
'count_real': instance.count_real,
|
'count_real': instance.count_real, 'route': instance.route,
|
||||||
'count_ok': instance.count_ok, 'count_notok': instance.count_notok,
|
'count_ok': instance.count_ok, 'count_notok': instance.count_notok,
|
||||||
'count_break_t': instance.count_break_t,
|
'count_break_t': instance.count_break_t,
|
||||||
'qct': instance.qct
|
'qct': instance.qct
|
||||||
|
@ -657,8 +659,9 @@ class MlogInitSerializer(CustomModelSerializer):
|
||||||
attrs['material_out'] = None
|
attrs['material_out'] = None
|
||||||
else:
|
else:
|
||||||
route: Route = attrs.get('route', None)
|
route: Route = attrs.get('route', None)
|
||||||
if not route:
|
# if not route:
|
||||||
raise ParseError('缺少工艺路线')
|
# raise ParseError('缺少工艺路线')
|
||||||
|
if route:
|
||||||
if route and route.process != mgroup.process:
|
if route and route.process != mgroup.process:
|
||||||
raise ParseError('工序不匹配')
|
raise ParseError('工序不匹配')
|
||||||
attrs['hour_work'] = route.hour_work
|
attrs['hour_work'] = route.hour_work
|
||||||
|
@ -671,7 +674,8 @@ class MlogInitSerializer(CustomModelSerializer):
|
||||||
raise ParseError('外协必须选择外协单位')
|
raise ParseError('外协必须选择外协单位')
|
||||||
if attrs.get('work_end_time', None):
|
if attrs.get('work_end_time', None):
|
||||||
attrs['handle_date'] = localdate(attrs['work_end_time'])
|
attrs['handle_date'] = localdate(attrs['work_end_time'])
|
||||||
if attrs["material_out"]:
|
# 如果已经确定产出,则自动获取qct
|
||||||
|
if attrs.get("material_out", None):
|
||||||
attrs["qct"] = Qct.get(attrs["material_out"], "process")
|
attrs["qct"] = Qct.get(attrs["material_out"], "process")
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
@ -703,7 +707,7 @@ class MlogbInSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Mlogb
|
model = Mlogb
|
||||||
fields = ['id', 'mlog', 'mtask', 'wm_in', 'count_use', 'count_pn_jgqbl',
|
fields = ['id', 'mlog', 'mtask', 'route', 'wm_in', 'count_use', 'count_pn_jgqbl',
|
||||||
'count_break', 'note', "parent", "mlogbdefect", "count_json_from"]
|
'count_break', 'note', "parent", "mlogbdefect", "count_json_from"]
|
||||||
extra_kwargs = {'count_use': {'required': True}, 'mtask': {'required': False},
|
extra_kwargs = {'count_use': {'required': True}, 'mtask': {'required': False},
|
||||||
'wm_in': {'required': True, "allow_empty": False}}
|
'wm_in': {'required': True, "allow_empty": False}}
|
||||||
|
@ -726,9 +730,19 @@ class MlogbInSerializer(CustomModelSerializer):
|
||||||
raise ParseError('返修或复检需使用返修品/不合格品')
|
raise ParseError('返修或复检需使用返修品/不合格品')
|
||||||
elif wm_in.state != WMaterial.WM_OK:
|
elif wm_in.state != WMaterial.WM_OK:
|
||||||
raise ParseError('非合格品不可使用')
|
raise ParseError('非合格品不可使用')
|
||||||
if mtask and mlog.route != mtask.route:
|
|
||||||
raise ParseError('工序不匹配')
|
mlog_route = mlog.route
|
||||||
route = mlog.route
|
if mlog_route is None:
|
||||||
|
if is_fix is False:
|
||||||
|
route = attrs.get("route", None)
|
||||||
|
if not route:
|
||||||
|
raise ParseError("缺少工艺步骤")
|
||||||
|
else:
|
||||||
|
route = mlog_route
|
||||||
|
attrs["route"] = route
|
||||||
|
|
||||||
|
if mtask and route != mtask.route:
|
||||||
|
raise ParseError('工艺步骤与任务不匹配')
|
||||||
attrs['material_in'] = wm_in.material
|
attrs['material_in'] = wm_in.material
|
||||||
attrs['batch'] = wm_in.batch
|
attrs['batch'] = wm_in.batch
|
||||||
attrs["batch_ofrom"] = wm_in.batch_ofrom
|
attrs["batch_ofrom"] = wm_in.batch_ofrom
|
||||||
|
|
|
@ -586,15 +586,18 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
mlogbin: Mlogb = serializer.save()
|
mlogbin: Mlogb = serializer.save()
|
||||||
mlog:Mlog = mlogbin.mlog
|
mlog:Mlog = mlogbin.mlog
|
||||||
route:Route = mlog.route
|
mgroup:Mgroup = mlog.mgroup
|
||||||
process: Process = route.process if route else None
|
route:Route = mlogbin.route
|
||||||
mtype = route.process.mtype if route else None
|
|
||||||
is_fix = mlog.is_fix
|
is_fix = mlog.is_fix
|
||||||
qct = mlog.qct
|
if route is None and is_fix is False:
|
||||||
|
raise ParseError('消耗物料缺失工艺步骤')
|
||||||
|
process: Process = mgroup.process if route else None
|
||||||
|
mtype = process.mtype if process else None
|
||||||
|
# qct = mlog.qct
|
||||||
# 以及mlogbw
|
# 以及mlogbw
|
||||||
material_in:Material = mlogbin.material_in
|
material_in:Material = mlogbin.material_in
|
||||||
# 如果是返修,则输出和输入相同
|
# 如果是返修,则输出和输入相同
|
||||||
material_out:Material = mlog.material_out if is_fix is False else material_in
|
material_out:Material = material_in if is_fix else route.material_out
|
||||||
if material_out is None:
|
if material_out is None:
|
||||||
raise ParseError('产物不可为空')
|
raise ParseError('产物不可为空')
|
||||||
# 如果是主要输入物料且是主批次,才需生成输出
|
# 如果是主要输入物料且是主批次,才需生成输出
|
||||||
|
@ -608,24 +611,33 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
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 qct is None:
|
# if qct is None:
|
||||||
mlog.qct = Qct.get(material_out, "process")
|
# mlog.qct = Qct.get(material_out, "process")
|
||||||
mlog.save(update_fields = ["qct"])
|
# mlog.save(update_fields = ["qct"])
|
||||||
|
|
||||||
m_dict = {
|
m_dict = {
|
||||||
"mtask": mlogbin.mtask,
|
"mtask": mlogbin.mtask,
|
||||||
|
"route": route,
|
||||||
"mlog": mlog,
|
"mlog": mlog,
|
||||||
"material_out": material_out,
|
"material_out": material_out,
|
||||||
"batch": mlogbin.batch,
|
"batch": mlogbin.batch,
|
||||||
"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom,
|
"batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom,
|
||||||
"qct": qct
|
"qct": Qct.get(material_out, "process")
|
||||||
}
|
}
|
||||||
if mtype == Process.PRO_DIV and material_in.tracking == Material.MA_TRACKING_SINGLE:
|
if mtype == Process.PRO_DIV and material_in.tracking == Material.MA_TRACKING_SINGLE:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog)
|
m_dict['batch'] = generate_new_batch(mlogbin.batch, mlog)
|
||||||
|
|
||||||
if mtype == Process.PRO_NORMAL: # 正常 支持批到批, 个到个
|
if is_fix:# 支持批到批,个到个
|
||||||
|
d_count_real = mlogbin.count_use-mlogbin.count_pn_jgqbl
|
||||||
|
d_count_ok = d_count_real
|
||||||
|
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": d_count_real, "count_ok": d_count_ok, "count_ok_full": d_count_ok}))
|
||||||
|
if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE:
|
||||||
|
for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"):
|
||||||
|
wpr_ = mlogbwin.wpr
|
||||||
|
Mlogbw.objects.get_or_create(wpr=wpr_, mlogb=mlogbout, defaults={"number": wpr_.number, "mlogbw_from": mlogbwin})
|
||||||
|
elif mtype == Process.PRO_NORMAL: # 正常 支持批到批, 个到个
|
||||||
d_count_real = mlogbin.count_use - mlogbin.count_pn_jgqbl
|
d_count_real = mlogbin.count_use - mlogbin.count_pn_jgqbl
|
||||||
d_count_ok = d_count_real
|
d_count_ok = d_count_real
|
||||||
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=
|
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=
|
||||||
|
@ -701,14 +713,6 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
else:
|
else:
|
||||||
numberx = f'{number}-{i+1}'
|
numberx = f'{number}-{i+1}'
|
||||||
Mlogbw.objects.get_or_create(number=numberx, mlogb=mlogbout)
|
Mlogbw.objects.get_or_create(number=numberx, mlogb=mlogbout)
|
||||||
elif is_fix:# 支持批到批,个到个
|
|
||||||
d_count_real = mlogbin.count_use-mlogbin.count_pn_jgqbl
|
|
||||||
d_count_ok = d_count_real
|
|
||||||
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": d_count_real, "count_ok": d_count_ok, "count_ok_full": d_count_ok}))
|
|
||||||
if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE:
|
|
||||||
for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"):
|
|
||||||
wpr_ = mlogbwin.wpr
|
|
||||||
Mlogbw.objects.get_or_create(wpr=wpr_, mlogb=mlogbout, defaults={"number": wpr_.number, "mlogbw_from": mlogbwin})
|
|
||||||
else:
|
else:
|
||||||
raise ParseError("不支持生成产出物料!")
|
raise ParseError("不支持生成产出物料!")
|
||||||
mlog.cal_mlog_count_from_mlogb()
|
mlog.cal_mlog_count_from_mlogb()
|
||||||
|
|
Loading…
Reference in New Issue