From d766d880e7caee84bd64622b00aa2a8e5f0a293e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 24 Jun 2024 18:23:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20utask=E6=94=AF=E6=8C=81=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E4=BB=BB=E5=8A=A1/=E6=8E=92=E4=BA=A7=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E4=BC=A0=E5=85=A5to=5Fday?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/pm/serializers.py | 11 +++- apps/pm/services.py | 135 +++++++++++++++++++++++++---------------- apps/pm/views.py | 27 ++++++--- 3 files changed, 111 insertions(+), 62 deletions(-) diff --git a/apps/pm/serializers.py b/apps/pm/serializers.py index 4ebc9e0f..f41c0b45 100644 --- a/apps/pm/serializers.py +++ b/apps/pm/serializers.py @@ -3,7 +3,7 @@ from rest_framework.exceptions import ValidationError, ParseError import math from apps.mtm.serializers import MaterialSimpleSerializer -from apps.pm.models import Mtask, Utask +from apps.pm.models import Mtask, Utask, SCHEDULE_TYPE from apps.sam.models import OrderItem from apps.utils.serializers import CustomModelSerializer from apps.system.models import Dept @@ -80,6 +80,15 @@ class MtaskSerializer(CustomModelSerializer): 'number', 'count', 'start_date', 'end_date']} return super().update(instance, new_data) +class SchedueMtasksSerializer(serializers.Serializer): + ids = serializers.ListField( + child=serializers.CharField(max_length=20), label="utask ID列表") + schedule_type = serializers.ChoiceField(label='排产类型', choices=SCHEDULE_TYPE, required=False) + + def validate(self, attrs): + if "schedule_type" not in attrs: + attrs["schedule_type"] = "to_day" + return super().validate(attrs) class SchedueSerializer(serializers.Serializer): orderitems = serializers.PrimaryKeyRelatedField( diff --git a/apps/pm/services.py b/apps/pm/services.py index 8dbabdce..c8147dad 100644 --- a/apps/pm/services.py +++ b/apps/pm/services.py @@ -53,9 +53,10 @@ class PmService: OrderItem.objects.filter(id__in=orderitemIds).update(utask=utask) @classmethod - def schedue_mtasks(cls, user, utask: Utask): + def schedue_mtasks(cls, user, utask: Utask, schedule_type: str = "to_day"): """ - 从大任务自动排产出小任务 + param schedule_type: to_day/to_mgroup + 从大任务自动排产出小任务(按工段/按天分配) """ # from apps.wpm.services import make_sflogs if utask.state != Utask.UTASK_CREATED: @@ -66,27 +67,42 @@ class PmService: # 计算相差天数 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) + if schedule_type == 'to_day': + 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 + }) + elif schedule_type == 'to_mgroup': + Mtask.objects.create(**{ + 'type': utask.type, + 'number': f'{number}_r1', + 'material_out': product, + 'material_in': utask.material_in, + 'mgroup': utask.mgroup, + 'count': count, + 'start_date': start_date, + 'end_date': end_date, + 'utask': utask, + 'create_by': user, + 'update_by': user, + 'is_count_utask': True + }) + else: + raise ParseError('不支持的排产类型') else: # 获取产品的加工路线 rqs = Route.get_routes(product) @@ -112,35 +128,50 @@ class PmService: 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) - + if schedule_type == 'to_day': + 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 + }) + elif schedule_type == 'to_mgroup': + Mtask.objects.create(**{ + 'number': f'{number}_r{ind+1}', + 'type': utask.type, + 'material_out': halfgood, + 'material_in': material_in, + 'mgroup': mgroup, + 'count': count, + 'start_date': start_date, + 'end_date': end_date, + 'utask': utask, + 'create_by': user, + 'update_by': user, + 'hour_work': val.hour_work, + 'is_count_utask': val.is_count_utask + }) + else: + raise ParseError('不支持的排产类型') @classmethod def check_orderitems(cls, orderitems: QuerySet[OrderItem]): """ diff --git a/apps/pm/views.py b/apps/pm/views.py index 93586885..396f59bf 100644 --- a/apps/pm/views.py +++ b/apps/pm/views.py @@ -9,7 +9,8 @@ from apps.utils.viewsets import CustomModelViewSet from .filters import MtaskFilter, UtaskFilter from .models import Mtask, Utask -from .serializers import MtaskSerializer, SchedueSerializer, UtaskSerializer, MtaskDaySerializer, MtaskAddInfoSerializer +from .serializers import (MtaskSerializer, SchedueSerializer, UtaskSerializer, + MtaskDaySerializer, MtaskAddInfoSerializer, SchedueMtasksSerializer) from .services import PmService from django.utils import timezone @@ -33,12 +34,12 @@ class UtaskViewSet(CustomModelViewSet): raise ParseError('该任务状态不可删除') return super().perform_destroy(instance) - @action(methods=['post'], detail=True, perms_map={'post': 'utask.stop'}, serializer_class=Serializer) + @action(methods=['post'], detail=True, perms_map={'post': 'utask.toggle'}, serializer_class=Serializer) @transaction.atomic - def stop(self, request, *args, **kwargs): - """停止任务 + def toggle(self, request, *args, **kwargs): + """停止/恢复任务 - 停止任务 + 停止/恢复任务 """ obj = self.get_object() user = request.user @@ -51,8 +52,16 @@ class UtaskViewSet(CustomModelViewSet): # 停止所有小任务 Mtask.objects.filter( utask=obj, state=Mtask.MTASK_ASSGINED).update(state=Mtask.MTASK_STOP, update_by=user, update_time=now) + elif obj.state == Utask.UTASK_STOP: + obj.state = Utask.UTASK_WORKING + obj.update_by = user + obj.update_time = now + obj.save() + # 恢复所有小任务 + Mtask.objects.filter( + utask=obj, state=Mtask.MTASK_STOP).update(state=Mtask.MTASK_ASSGINED, update_by=user, update_time=now) else: - raise ParseError('该状态下不可停止') + raise ParseError('该状态下不可变更') return Response() @action(methods=['post'], detail=False, perms_map={'post': 'utask.schedue'}, serializer_class=SchedueSerializer) @@ -69,10 +78,10 @@ class UtaskViewSet(CustomModelViewSet): request.data['orderitems'], vdata['start_date'], vdata['end_date'], vdata.get('belong_dept', None)) return Response() - @action(methods=['post'], detail=False, perms_map={'post': 'utask.schedue'}, serializer_class=PkSerializer) + @action(methods=['post'], detail=False, perms_map={'post': 'utask.schedue'}, serializer_class=SchedueMtasksSerializer) @transaction.atomic def schedue_mtasks(self, request, *args, **kwargs): - """任务分解 + """任务分解(支持分解到天或仅仅到工段) 任务分解 """ @@ -82,7 +91,7 @@ class UtaskViewSet(CustomModelViewSet): utasks = Utask.objects.filter( id__in=vdata['ids'], state=Utask.UTASK_CREATED) for i in utasks: - PmService.schedue_mtasks(request.user, i) + PmService.schedue_mtasks(request.user, i, vdata["schedule_type"]) return Response() @action(methods=['post'], detail=False, perms_map={'post': 'utask.assgin'}, serializer_class=PkSerializer)