231 lines
10 KiB
Python
231 lines
10 KiB
Python
from apps.sam.models import OrderItem
|
|
from apps.mtm.models import Route, Material, Mgroup
|
|
from rest_framework.exceptions import ParseError
|
|
from apps.pm.models import Mtask, Utask
|
|
from django.db.models.query import QuerySet
|
|
from datetime import date, timedelta
|
|
import math
|
|
from typing import List
|
|
|
|
|
|
class PmService:
|
|
|
|
@classmethod
|
|
def make_utasks_from_orderitems(cls, user, orderitemIds: List[str], start_date: date, end_date: date):
|
|
start_date_str = start_date.strftime('%Y%m%d')
|
|
if start_date >= end_date:
|
|
raise ParseError('开始时间不可大于结束时间')
|
|
orderitems = OrderItem.objects.filter(pk__in=orderitemIds)
|
|
if orderitems.exclude(utask=None).exists():
|
|
raise ParseError('存在订单项已排任务!')
|
|
rdict = {}
|
|
for item in orderitems:
|
|
productId = item.material.id
|
|
if productId not in rdict:
|
|
rdict[productId] = [item.material, item.count, [item.id]]
|
|
else:
|
|
rdict[productId][1] = rdict[productId][1] + item.count
|
|
rdict[productId][2].append(item.id)
|
|
# 生成大任务
|
|
make_list = []
|
|
for k, v in rdict.items():
|
|
xproduct, xcount, orderitemsId = v
|
|
if xproduct.is_assemb:
|
|
for key in xproduct.components:
|
|
make_list.append([Material.objects.get(
|
|
id=key), xcount*xproduct.components[key], orderitemsId])
|
|
else:
|
|
make_list = [[xproduct, xcount, orderitemsId]]
|
|
for i in make_list:
|
|
material, count, orderitemsId = i
|
|
utask = Utask.objects.create(
|
|
number=f'{material.number}_{start_date_str}',
|
|
material=material,
|
|
count=count,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
create_by=user,
|
|
update_by=user
|
|
)
|
|
OrderItem.objects.filter(id__in=orderitemIds).update(utask=utask)
|
|
|
|
@classmethod
|
|
def schedue_mtasks(cls, user, utask: Utask):
|
|
"""
|
|
从大任务自动排产出小任务
|
|
"""
|
|
number, product, count, start_date, end_date = utask.number, utask.material, utask.count, utask.start_date, utask.end_date
|
|
# 计算相差天数
|
|
rela_days = (end_date - start_date).days + 1
|
|
# 获取每个产品的加工路线
|
|
rqs = Route.get_routes(product)
|
|
# 创建小任务
|
|
for ind, val in enumerate(rqs):
|
|
if val.material_out:
|
|
halfgood = val.material_out
|
|
else:
|
|
raise ParseError(f'第{ind+1}步-无输出物料')
|
|
if val.material_in:
|
|
material_in = val.material_in
|
|
elif ind > 0:
|
|
material_in = rqs[ind-1].material_out
|
|
if val.is_autotask:
|
|
# 找到工段
|
|
mgroups = Mgroup.objects.filter(process=val.process)
|
|
mgroups_count = mgroups.count()
|
|
if mgroups_count == 1:
|
|
mgroup = mgroups.first()
|
|
elif mgroups_count == 0:
|
|
raise ParseError(f'第{ind+1}步-工段不存在!')
|
|
else: # 后面可能会指定车间
|
|
raise ParseError(f'第{ind+1}步-工段存在多个!')
|
|
task_count = count
|
|
if val.out_rate:
|
|
if val.out_rate > 1:
|
|
task_count = math.ceil(
|
|
count / (val.out_rate/100))
|
|
else:
|
|
task_count = math.ceil(count / val.out_rate)
|
|
task_count_day = math.ceil(task_count/rela_days)
|
|
if rela_days > 1:
|
|
for i in range(rela_days):
|
|
task_date = start_date + timedelta(days=i)
|
|
Mtask.objects.create(**{
|
|
'number': f'{number}_r{ind+1}_{i+1}',
|
|
'material_out': halfgood,
|
|
'material_in': material_in,
|
|
'mgroup': mgroup,
|
|
'count': task_count_day,
|
|
'start_date': task_date,
|
|
'end_date': task_date,
|
|
'utask': utask,
|
|
'create_by': user,
|
|
'update_by': user
|
|
})
|
|
|
|
@classmethod
|
|
def check_orderitems(cls, orderitems: QuerySet[OrderItem]):
|
|
"""
|
|
废弃
|
|
校验orderitems并返回整合后的字典以productId为key, [product, count, end_date, orderitems] 为value
|
|
"""
|
|
rdict = {}
|
|
if orderitems.exclude(mtask=None).exists():
|
|
raise ParseError('存在订单项已排任务!')
|
|
for item in orderitems:
|
|
productId = item.material.id
|
|
if productId not in rdict:
|
|
rdict[productId] = [item.material, item.count,
|
|
item.order.delivery_date, [item.id]]
|
|
else:
|
|
order_date = item.order.delivery_date
|
|
if rdict[productId][2] > order_date:
|
|
rdict[productId][2] = order_date
|
|
rdict[productId][1] = rdict[productId][1] + item.count
|
|
rdict[productId][3].append(item.id)
|
|
return rdict
|
|
|
|
@classmethod
|
|
def schedue_from_orderitems(cls, user, orderitemIds: List[str], start_date: date, end_date: date = None):
|
|
"""
|
|
废弃
|
|
从多个订单明细自动排产
|
|
"""
|
|
orderitems = OrderItem.objects.filter(pk__in=orderitemIds)
|
|
rdict = cls.check_orderitems(orderitems)
|
|
start_date_str = start_date.strftime('%Y%m%d')
|
|
for k, v in rdict.items():
|
|
xproduct, xcount, end_date_cal, orderitemId_list = v
|
|
if end_date is None:
|
|
end_date = end_date_cal
|
|
if start_date >= end_date:
|
|
raise ParseError('开始时间不可大于结束时间')
|
|
# 计算相差天数
|
|
rela_days = (end_date - start_date).days + 1
|
|
make_list = []
|
|
if xproduct.is_assemb:
|
|
for key in xproduct.components:
|
|
make_list.append([Material.objects.get(
|
|
id=key), xcount*xproduct.components[key], end_date, orderitemId_list])
|
|
else:
|
|
make_list = [[xproduct, xcount, end_date, orderitemId_list]]
|
|
for i in make_list:
|
|
product, count, end_date, orderitemId_list = i
|
|
# 获取每个产品的加工路线
|
|
rqs = Route.get_routes(product)
|
|
lasttask = None
|
|
# 创建父任务
|
|
for ind, val in enumerate(rqs):
|
|
if val.material_out:
|
|
halfgood = val.material_out
|
|
else:
|
|
raise ParseError(f'第{ind+1}步-无输出物料')
|
|
if val.material_in:
|
|
material_in = val.material_in
|
|
elif ind > 0:
|
|
material_in = rqs[ind-1].material_out
|
|
if val.is_autotask:
|
|
# 找到工段
|
|
mgroups = Mgroup.objects.filter(process=val.process)
|
|
mgroups_count = mgroups.count()
|
|
if mgroups_count == 1:
|
|
mgroup = mgroups.first()
|
|
elif mgroups_count == 0:
|
|
raise ParseError(f'第{ind+1}步-工段不存在!')
|
|
else: # 后面可能会指定车间
|
|
raise ParseError(f'第{ind+1}步-工段存在多个!')
|
|
task_count = count
|
|
if val.out_rate:
|
|
if val.out_rate > 1:
|
|
task_count = math.ceil(
|
|
count / (val.out_rate/100))
|
|
else:
|
|
task_count = math.ceil(count / val.out_rate)
|
|
fmtask = Mtask.objects.create(**{
|
|
'number': f'{product.number}_{start_date_str}_r{ind+1}',
|
|
'material_out': halfgood,
|
|
'material_in': material_in,
|
|
'mgroup': mgroup,
|
|
'count': task_count,
|
|
'start_date': start_date,
|
|
'end_date': end_date,
|
|
'create_by': user,
|
|
'update_by': user,
|
|
})
|
|
task_count_day = math.ceil(task_count/rela_days)
|
|
if rela_days > 1:
|
|
for i in range(rela_days):
|
|
task_date = start_date + timedelta(days=i)
|
|
Mtask.objects.create(**{
|
|
'number': f'{fmtask.number}_{i+1}',
|
|
'material_out': halfgood,
|
|
'material_in': material_in,
|
|
'mgroup': mgroup,
|
|
'count': task_count_day,
|
|
'start_date': task_date,
|
|
'end_date': task_date,
|
|
'parent': fmtask,
|
|
'create_by': user,
|
|
'update_by': user
|
|
})
|
|
lasttask = fmtask
|
|
OrderItem.objects.filter(
|
|
id__in=orderitemId_list).update(mtask=lasttask)
|
|
from apps.sam.tasks import change_order_state_when_schedue
|
|
change_order_state_when_schedue(orderitemIds)
|
|
|
|
@classmethod
|
|
def mtasks_submit(cls, mtasks: QuerySet[Mtask]):
|
|
"""
|
|
锁定生产任务
|
|
"""
|
|
from apps.wpm.models import Mlog
|
|
from apps.wpm.services import mlog_confirm, update_mtask
|
|
for mtask in mtasks:
|
|
mlogs = Mlog.objects.filter(mtask=mtask)
|
|
for mlog in mlogs:
|
|
mlog_confirm(mlog)
|
|
update_mtask(mtask)
|
|
mtask.state = Mtask.MTASK_DONE
|
|
mtask.save()
|