feat: route增加主要输入和输出以及自动排产逻辑修改
This commit is contained in:
parent
ad84876712
commit
5e34558c02
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 3.2.12 on 2023-10-16 08:48
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0015_mgroup_need_enm'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='route',
|
||||
name='material_in',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='route_material_in', to='mtm.material', verbose_name='主要输入物料'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='route',
|
||||
name='material_out',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='route_material_out', to='mtm.material', verbose_name='主要输出物料'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='route',
|
||||
name='material',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联产品'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='route',
|
||||
name='process',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='工序'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -1,6 +1,7 @@
|
|||
from django.db import models
|
||||
from apps.system.models import CommonAModel, Dictionary, CommonBModel, CommonADModel, File, BaseModel
|
||||
from django.db.models import Subquery, OuterRef
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
class Process(CommonBModel):
|
||||
|
@ -140,11 +141,15 @@ class Route(CommonAModel):
|
|||
工艺路线
|
||||
"""
|
||||
material = models.ForeignKey(
|
||||
Material, verbose_name='关联产品', on_delete=models.CASCADE, null=True, blank=True)
|
||||
Material, verbose_name='关联产品', on_delete=models.CASCADE)
|
||||
process = models.ForeignKey(
|
||||
Process, verbose_name='工序', on_delete=models.CASCADE, null=True, blank=True)
|
||||
Process, verbose_name='工序', on_delete=models.CASCADE)
|
||||
sort = models.PositiveSmallIntegerField('顺序', default=1)
|
||||
is_autotask = models.BooleanField('是否自动排产', default=False)
|
||||
material_in = models.ForeignKey(
|
||||
Material, verbose_name='主要输入物料', on_delete=models.CASCADE, related_name='route_material_in', null=True, blank=True)
|
||||
material_out = models.ForeignKey(
|
||||
Material, verbose_name='主要输出物料', on_delete=models.CASCADE, related_name='route_material_out', null=True, blank=True)
|
||||
out_rate = models.FloatField('出材率', default=100, null=True, blank=True)
|
||||
|
||||
@staticmethod
|
||||
|
@ -153,6 +158,11 @@ class Route(CommonAModel):
|
|||
返回工艺路线带车间
|
||||
"""
|
||||
kwargs = {'material': material}
|
||||
# 校验工艺路线是否正常
|
||||
rq = Route.objects.filter(
|
||||
**kwargs).order_by('sort', 'process__sort', 'create_time')
|
||||
if rq.first().material_in is None or rq.last().material_out is None or rq.last().material_out != rq.last().material:
|
||||
raise ValidationError('首步缺少输入/最后一步缺少输出')
|
||||
if autotask:
|
||||
kwargs['is_autotask'] = True
|
||||
return Route.objects.annotate(mgroups=Subquery(Mgroup.objects.filter(process=OuterRef('pk')))).filter(**kwargs).order_by('sort', 'process__sort' 'create_time')
|
||||
return Route.objects.annotate(mgroups=Subquery(Mgroup.objects.filter(process=OuterRef('pk')))).filter(**kwargs).order_by('sort', 'process__sort', 'create_time')
|
||||
|
|
|
@ -87,7 +87,26 @@ class RouteSerializer(CustomModelSerializer):
|
|||
raise ValidationError('请选择最终产品')
|
||||
return super().validate(attrs)
|
||||
|
||||
def gen_material_out(self, instance):
|
||||
instance.material_out, _ = Material.objects.get_or_create(type=Material.MA_TYPE_HALFGOOD, parent=instance.material, process=instance.process,
|
||||
defaults={'parent': instance.material, 'process': instance.process,
|
||||
'is_hidden': True, 'name': instance.material.name,
|
||||
'number': instance.material.number,
|
||||
'specification': instance.material.specification,
|
||||
'type': Material.MA_TYPE_HALFGOOD,
|
||||
'create_by': self.request.user,
|
||||
'update_by': self.request.user,
|
||||
})
|
||||
instance.save()
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
self.gen_material_out()
|
||||
return instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
validated_data.pop('material', None)
|
||||
validated_data.pop('process', None)
|
||||
return super().update(instance, validated_data)
|
||||
instance = super().update(instance, validated_data)
|
||||
self.gen_material_out()
|
||||
return instance
|
||||
|
|
|
@ -18,9 +18,9 @@ class MtaskFilter(filters.FilterSet):
|
|||
"mgroup__cate": ["exact"],
|
||||
"mgroup__process": ["exact"],
|
||||
"mgroup__process__cate": ["exact"],
|
||||
"material": ["exact"],
|
||||
"material__type": ["exact"],
|
||||
"material__is_hidden": ["exact"],
|
||||
"material_out": ["exact"],
|
||||
"material_out__type": ["exact"],
|
||||
"material_out__is_hidden": ["exact"],
|
||||
"parent": ["exact", "isnull"]
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 3.2.12 on 2023-10-16 08:48
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0016_auto_20231016_1648'),
|
||||
('pm', '0005_alter_mtask_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='mtask',
|
||||
name='material',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='mtask',
|
||||
name='material_before',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mtask',
|
||||
name='material_in',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mtask_material_in', to='mtm.material', verbose_name='领用物'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mtask',
|
||||
name='material_out',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='mtask_material_out', to='mtm.material', verbose_name='产物'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -24,10 +24,10 @@ class Mtask(CommonADModel):
|
|||
number = models.CharField('编号', max_length=50, unique=True)
|
||||
mgroup = models.ForeignKey(
|
||||
Mgroup, verbose_name='工段', on_delete=models.CASCADE)
|
||||
material_before = models.ForeignKey(
|
||||
Material, verbose_name='领用物', on_delete=models.CASCADE, related_name='mtask_material_before', null=True, blank=True)
|
||||
material = models.ForeignKey(
|
||||
Material, verbose_name='产物', on_delete=models.CASCADE, related_name='mtask_material')
|
||||
material_in = models.ForeignKey(
|
||||
Material, verbose_name='领用物', on_delete=models.CASCADE, related_name='mtask_material_in', null=True, blank=True)
|
||||
material_out = models.ForeignKey(
|
||||
Material, verbose_name='产物', on_delete=models.CASCADE, related_name='mtask_material_out')
|
||||
count = models.PositiveIntegerField('任务数', default=1)
|
||||
count_real = models.PositiveIntegerField('实际生产数', default=0)
|
||||
count_ok = models.PositiveIntegerField('合格数', default=0)
|
||||
|
|
|
@ -48,12 +48,16 @@ class PmService:
|
|||
rela_days = (end_date - start_date).days + 1
|
||||
# 获取工艺路线
|
||||
rqs = Route.get_routes(product)
|
||||
last_route = rqs.last() # 最后一步是产生产品
|
||||
# 创建父任务
|
||||
for ind, val in enumerate(rqs):
|
||||
material_before = None
|
||||
if ind > 0:
|
||||
material_before = rqs[ind-1].material
|
||||
if val.material_out:
|
||||
halfgood = val.material_out
|
||||
else:
|
||||
raise ParseError(f'第{ind+1}步-无输出物料')
|
||||
if val.material_in:
|
||||
material_before = val.material_in
|
||||
elif ind > 0:
|
||||
material_before = rqs[ind-1].material_out
|
||||
if val.is_autotask:
|
||||
# 找到工段
|
||||
mgroups = val.mgroups
|
||||
|
@ -61,21 +65,9 @@ class PmService:
|
|||
if mgroups_count == 1:
|
||||
mgroup = mgroups.first()
|
||||
elif mgroups_count == 0:
|
||||
raise ParseError(f'{val.name}-工段不存在!')
|
||||
raise ParseError(f'第{ind+1}步-工段不存在!')
|
||||
else: # 后面可能会指定车间
|
||||
raise ParseError(f'{val.name}-工段存在多个!')
|
||||
# 找到存在的半成品
|
||||
halfgood, _ = Material.objects.get_or_create(type=Material.MA_TYPE_HALFGOOD, parent=product, mgroup=mgroup,
|
||||
defaults={'parent': product, 'mgroup': mgroup,
|
||||
'is_hidden': True, 'name': product.name,
|
||||
'number': product.number,
|
||||
'specification': product.specification,
|
||||
'type': Material.MA_TYPE_HALFGOOD,
|
||||
'create_by': user,
|
||||
'update_by': user,
|
||||
})
|
||||
if val == last_route:
|
||||
halfgood = product
|
||||
raise ParseError(f'第{ind+1}步-工段存在多个!')
|
||||
task_count = count
|
||||
if val.out_rate:
|
||||
if val.out_rate > 1:
|
||||
|
|
|
@ -23,7 +23,7 @@ class MtaskViewSet(CustomModelViewSet):
|
|||
queryset = Mtask.objects.all()
|
||||
serializer_class = MtaskSerializer
|
||||
filterset_class = MtaskFilter
|
||||
select_related_fields = ['material', 'mgroup']
|
||||
select_related_fields = ['material_in', 'material_out', 'mgroup']
|
||||
ordering_fields = ['start_date', 'mgroup__process__sort']
|
||||
ordering = ['mgroup__process__sort', '-start_date', '-create_time']
|
||||
|
||||
|
|
|
@ -110,27 +110,27 @@ def mlog_confirm(mlog: Mlog):
|
|||
"""
|
||||
mtask = mlog.mtask
|
||||
belong_dept = mtask.belong_dept
|
||||
material = mtask.material
|
||||
material_before = mtask.material_before
|
||||
if material_before and material_before.is_hidden is False: # 需要进行车间库存管理
|
||||
material_out = mtask.material_out
|
||||
material_in = mtask.material_in
|
||||
if material_in and material_in.is_hidden is False: # 需要进行车间库存管理
|
||||
# 需要判断领用数是否合理
|
||||
material_has_qs = WMaterial.objects.filter(
|
||||
batch=mlog.batch, material=material_before, belong_dept=belong_dept)
|
||||
batch=mlog.batch, material=material_in, belong_dept=belong_dept)
|
||||
count_x = material_has_qs.count()
|
||||
if count_x == 1:
|
||||
material_has = material_has_qs.first()
|
||||
elif count_x == 0:
|
||||
raise ParseError(f'{material_before.name}-{mlog.batch}-批次库存不存在!')
|
||||
raise ParseError(f'{material_in.name}-{mlog.batch}-批次库存不存在!')
|
||||
else:
|
||||
raise ParseError(f'{material_before.name}-{mlog.batch}-存在多个相同批次!')
|
||||
raise ParseError(f'{material_in.name}-{mlog.batch}-存在多个相同批次!')
|
||||
if mlog.count_use > material_has.count:
|
||||
raise ParseError(f'{material_before.name}-{mlog.batch}-该批次车间库存不足!')
|
||||
raise ParseError(f'{material_in.name}-{mlog.batch}-该批次车间库存不足!')
|
||||
else:
|
||||
material_has.count = material_has.count - mlog.count_use
|
||||
material_has.save()
|
||||
if material.is_hidden is False: # 需要入库
|
||||
wmaterial, _ = WMaterial.objects.get_or_create(batch=mlog.batch, material=material, belong_dept=belong_dept, defaults={
|
||||
'batch': mlog.batch, 'material': material, 'belong_dept': belong_dept
|
||||
if material_out.is_hidden is False: # 需要入库
|
||||
wmaterial, _ = WMaterial.objects.get_or_create(batch=mlog.batch, material=material_out, belong_dept=belong_dept, defaults={
|
||||
'batch': mlog.batch, 'material': material_out, 'belong_dept': belong_dept
|
||||
})
|
||||
wmaterial.count = wmaterial.count + mlog.count_ok
|
||||
wmaterial.save()
|
||||
|
|
Loading…
Reference in New Issue