diff --git a/hb_server/apps/pm/migrations/0018_productionplan_process_json.py b/hb_server/apps/pm/migrations/0018_productionplan_process_json.py new file mode 100644 index 0000000..eaf0713 --- /dev/null +++ b/hb_server/apps/pm/migrations/0018_productionplan_process_json.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2021-12-27 05:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0017_auto_20211213_1401'), + ] + + operations = [ + migrations.AddField( + model_name='productionplan', + name='process_json', + field=models.JSONField(blank=True, default=list, null=True, verbose_name='按工序的统计数'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 7588491..d9bf331 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -29,6 +29,7 @@ class ProductionPlan(CommonAModel): count_ok = models.PositiveIntegerField('合格数', default=0) start_date = models.DateField('计划开工日期') end_date = models.DateField('计划完工日期') + process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True) is_planed = models.BooleanField('是否已排产', default=False) class Meta: verbose_name = '生产计划' diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 5a5b099..3f88285 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -48,11 +48,7 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): class GenSubPlanSerializer(serializers.Serializer): pass -class SubProductionProgressSerializer(serializers.ModelSerializer): - material_ = MaterialSimpleSerializer(source='material', read_only=True) - class Meta: - model = SubProductionProgress - fields = '__all__' + class PickNeedSerializer(serializers.Serializer): warehouse = serializers.IntegerField(label="仓库ID") @@ -64,3 +60,10 @@ class SubproductionPlanSimpleSerializer(serializers.ModelSerializer): class Meta: model = SubProductionPlan fields = ['id', 'number'] + +class SubProductionProgressSerializer(serializers.ModelSerializer): + material_ = MaterialSimpleSerializer(source='material', read_only=True) + subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) + class Meta: + model = SubProductionProgress + fields = '__all__' \ No newline at end of file diff --git a/hb_server/apps/pm/services.py b/hb_server/apps/pm/services.py new file mode 100644 index 0000000..0b6894d --- /dev/null +++ b/hb_server/apps/pm/services.py @@ -0,0 +1,27 @@ +from django.db.models.aggregates import Sum +from apps.pm.models import ProductionPlan, SubProductionPlan + +class PmService: + + @classmethod + def update_plan_process_json(cls, plan:ProductionPlan): + """ + 更新计划按工序统计字段 + """ + ret = {} + subplans = SubProductionPlan.objects.filter(production_plan=plan, is_deleted=False) + qs = subplans.values('process', 'process__name', 'process__number').annotate(count=Sum('main_count'), + count_real=Sum('main_count_real'), count_ok=Sum('main_count_ok')) + qs_list = list(qs) + for i in qs_list: + ret[i['process__number']] = { + 'count':i['count'], + 'count_real':i['count_real'], + 'count_ok':i['count_ok'], + 'rate': round((i['count_ok']/i['count_real'])*100,2) if i['count_real'] > 0 else 0 + } + plan.process_json = ret + plan.save() + + + diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py index c4871ee..afd8411 100644 --- a/hb_server/apps/pm/signals.py +++ b/hb_server/apps/pm/signals.py @@ -2,6 +2,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from apps.mtm.models import Material from apps.pm.models import SubProductionPlan, SubProductionProgress +from apps.pm.services import PmService @receiver(post_save, sender=SubProductionProgress) def update_subplan_main(sender, instance, created, **kwargs): @@ -13,6 +14,7 @@ def update_subplan_main(sender, instance, created, **kwargs): if created: subplan.main_product = instance.material subplan.main_count = instance.count + subplan.main_count_real = instance.count_real subplan.main_count_ok = instance.count_ok if instance.count_ok >= instance.count and instance.count_ok > 0: @@ -26,6 +28,10 @@ def update_subplan_main(sender, instance, created, **kwargs): plan.count_real = subplan.main_count_real plan.count_ok = subplan.main_count_ok plan.save() + # 更新计划工序统计字段 + PmService.update_plan_process_json(subplan.production_plan) + + diff --git a/hb_server/apps/wf/migrations/0023_auto_20211227_1318.py b/hb_server/apps/wf/migrations/0023_auto_20211227_1318.py new file mode 100644 index 0000000..c3833c3 --- /dev/null +++ b/hb_server/apps/wf/migrations/0023_auto_20211227_1318.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2021-12-27 05:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0022_alter_customfield_is_hidden'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='field_type', + field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file', max_length=50, verbose_name='类型'), + ), + migrations.AlterField( + model_name='customfield', + name='label', + field=models.CharField(default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项', max_length=1000, verbose_name='标签'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 36a1cb3..c5e0ce6 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -159,7 +159,7 @@ class CustomField(CommonAModel): ) workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') field_type = models.CharField('类型', max_length=50, choices=field_type_choices, - help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, cascader, cascaders,textarea, file') + help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file') field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突') field_name = models.CharField('字段名称', max_length=50) sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列') @@ -173,7 +173,7 @@ class CustomField(CommonAModel): field_choice = models.JSONField('选项值', default=list, blank=True, help_text='选项值,格式为list, 例["id":1, "name":"张三"]') - label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用') + label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项') # hook = models.CharField('hook', max_length=1000, default='', help_text='获取下拉选项用于动态选项值') is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的字段信息') diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 6daa326..f7769dd 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -129,21 +129,22 @@ class TicketDetailSerializer(serializers.ModelSerializer): i['field_state'] = state_fields.get(key, 1) i['field_value'] = ticket_data.get(key, None) i['field_display'] = i['field_value'] # 该字段是用于查看详情直接展示 - if 'sys_user' in i['label']: - if isinstance(i['field_value'], list): - i['field_display'] = ','.join(list(User.objects.filter(id__in=i['field_value']).values_list('name', flat=True))) - else: - i['field_display'] = User.objects.get(id=i['field_value']).name - elif i['field_type'] in ['radio', 'select']: - for m in i['field_choice']: - if m['id'] == i['field_value']: - i['field_display'] = m['name'] - elif i['field_type'] in ['checkbox', 'selects']: - d_list = [] - for m in i['field_choice']: - if m['id'] in i['field_value']: - d_list.append(m['name']) - i['field_display'] = ','.join(d_list) + if i['field_value']: + if 'sys_user' in i['label']: + if isinstance(i['field_value'], list): + i['field_display'] = ','.join(list(User.objects.filter(id__in=i['field_value']).values_list('name', flat=True))) + else: + i['field_display'] = User.objects.get(id=i['field_value']).name + elif i['field_type'] in ['radio', 'select']: + for m in i['field_choice']: + if m['id'] == i['field_value']: + i['field_display'] = m['name'] + elif i['field_type'] in ['checkbox', 'selects']: + d_list = [] + for m in i['field_choice']: + if m['id'] in i['field_value']: + d_list.append(m['name']) + i['field_display'] = ','.join(d_list) return all_fields_l def filter_display(self, item, field_value): diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 30ba1f2..04b8e87 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -1,4 +1,6 @@ from typing import List + +from django.db.models.expressions import F from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.mtm.models import Material, Step, SubprodctionMaterial from apps.qm.models import TestRecord @@ -55,13 +57,13 @@ class WpmServies(object): wproduct.number = 'WP'+ranstr(7) # 更新子计划合格进度 - instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, + ins = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_ok = instance.count_ok + 1 - instance.save() + ins.count_ok = ins.count_ok + 1 + ins.save() else:# 如果不合格 wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK # 需要走不合格品审理的工单 wproduct.update_by = user wproduct.test = None - wproduct.save() \ No newline at end of file + wproduct.save() diff --git a/hb_server/apps/wpm/signals.py b/hb_server/apps/wpm/signals.py index 3d27b38..6c61165 100644 --- a/hb_server/apps/wpm/signals.py +++ b/hb_server/apps/wpm/signals.py @@ -1,6 +1,7 @@ +from django.db.models.expressions import F from django.db.models.signals import post_save -from apps.mtm.models import SubprodctionMaterial -from apps.pm.models import SubProductionProgress +from apps.mtm.models import Step, SubprodctionMaterial +from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.qm.models import TestRecord from apps.wf.models import Ticket from django.dispatch import receiver @@ -56,10 +57,11 @@ def handleTicket(sender, instance, created, **kwargs): wp.ng_sign = decision if decision in [WProduct.NG_BACK_WORK, WProduct.NG_BACK_FIX]: - step = ticket_data['back_step'] + step = Step.objects.get(id=ticket_data['back_step']) wp.step = step # 找到当时所属的计划 - sp = OperationWproduct.objects.filter(operation__is_submited=True, operation__step=step).first() + sp = SubProductionPlan.objects.filter(ow_subplan__wproduct=wp, + ow_subplan__operation__is_submited=True, ow_subplan__step=step).first() if sp: wp.subproduction_plan = sp wt.save() @@ -67,10 +69,11 @@ def handleTicket(sender, instance, created, **kwargs): wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT wp.save() # 更新子计划合格进度 - instance = SubProductionProgress.objects.get(subproduction_plan=sp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_ok = instance.count_ok - 1 #进度计算这里该怎么处理呢 - instance.save() + if sp != wt.subproduction_plan: + ins = SubProductionProgress.objects.filter(subproduction_plan=sp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + ins.count_ok = ins.count_ok - 1 + ins.save() else: raise exceptions.APIException('返回步骤点错误') diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index ff06c97..a3b840c 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -1,3 +1,4 @@ +from django.db.models.expressions import F from django.shortcuts import render from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin @@ -370,7 +371,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 发起不合格审理单 """ obj = self.get_object() - if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None: + if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None\ + or obj.ticket is not None: raise exceptions.APIException('该产品不可发起不合格审理') workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first() if workflow: @@ -542,10 +544,10 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 更新子计划生产进度 # 如果产品有返工标记不做计算 if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: - instance = SubProductionProgress.objects.get(subproduction_plan=wsp, + ins = SubProductionProgress.objects.get(subproduction_plan=wsp, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 - instance.save() + ins.count_real = ins.count_real + 1 + ins.save() wp.operation = None wp.update_by = request.user wp.save()