276 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
	
| from apps.system.models import Dept, User
 | |
| 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 django.utils import timezone
 | |
| from datetime import date, timedelta
 | |
| import math
 | |
| from typing import List, Union
 | |
| 
 | |
| 
 | |
| class PmService:
 | |
| 
 | |
|     @classmethod
 | |
|     def make_utasks_from_orderitems(cls, user, orderitemIds: List[str], start_date: date, end_date: date, belong_dept: Union[Dept, None]):
 | |
|         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,
 | |
|                 belong_dept=belong_dept
 | |
|             )
 | |
|             OrderItem.objects.filter(id__in=orderitemIds).update(utask=utask)
 | |
| 
 | |
|     @classmethod
 | |
|     def schedue_mtasks(cls, user, utask: Utask):
 | |
|         """
 | |
|         从大任务自动排产出小任务
 | |
|         """
 | |
|         # from apps.wpm.services import make_sflogs
 | |
|         if utask.state != Utask.UTASK_CREATED:
 | |
|             raise ParseError('任务状态异常')
 | |
|         utask.state = Utask.UTASK_DECOMPOSE
 | |
|         utask.save()
 | |
|         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
 | |
|         if utask.mgroup:  # 如果存在指定的mgroup则直接排产
 | |
|             if rela_days >= 1:
 | |
|                 task_count_day = math.ceil(count/rela_days)
 | |
|                 for i in range(rela_days):
 | |
|                     task_date = start_date + timedelta(days=i)
 | |
|                     Mtask.objects.create(**{
 | |
|                         'type': utask.type,
 | |
|                         'number': f'{number}_{i+1}',
 | |
|                         'material_out': utask.material,
 | |
|                         'material_in': utask.material_in,
 | |
|                         'mgroup': utask.mgroup,
 | |
|                         'count': task_count_day,
 | |
|                         'start_date': task_date,
 | |
|                         'end_date':  task_date,
 | |
|                         'utask': utask,
 | |
|                         'create_by': user,
 | |
|                         'update_by': user,
 | |
|                         'is_count_utask': True
 | |
|                     })
 | |
|                     # 先撤销有需要再加
 | |
|                     # make_sflogs(mgroup=utask.mgroup,
 | |
|                     #             start_date=task_date, end_date=task_date, create_by=user)
 | |
|         else:
 | |
|             # 获取产品的加工路线
 | |
|             rqs = Route.get_routes(product)
 | |
|             if not rqs.exists():
 | |
|                 raise ParseError('未配置工艺路线')
 | |
|             # 创建小任务
 | |
|             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}',
 | |
|                                 'type': utask.type,
 | |
|                                 '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,
 | |
|                                 'is_count_utask': val.is_count_utask
 | |
|                             })
 | |
|                             # 先撤销,有需要再加
 | |
|                             # make_sflogs(mgroup=mgroup,
 | |
|                             #             start_date=task_date, end_date=task_date, create_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 mtask_submit(cls, mtask: Mtask, user: User):
 | |
|         """
 | |
|         锁定生产任务
 | |
|         """
 | |
|         from apps.wpm.models import Mlog
 | |
|         from apps.wpm.services import mlog_submit, update_mtask
 | |
|         now = timezone.now()
 | |
|         if mtask.state == Mtask.MTASK_ASSGINED:
 | |
|             mlogs = Mlog.objects.filter(mtask=mtask)
 | |
|             # if ignore_nologs is False and mlogs.count() == 0:
 | |
|             #     raise ParseError(f'{mtask.mgroup.name}_未填写日志')
 | |
|             for mlog in mlogs:
 | |
|                 mlog_submit(mlog, user, now)
 | |
|             mtask.state = Mtask.MTASK_SUBMIT
 | |
|             mtask.submit_time = now
 | |
|             mtask.submit_user = user
 | |
|             mtask.save()
 | |
|             update_mtask(mtask)
 | |
|         else:
 | |
|             raise ParseError('该任务状态不可提交')
 |