From 8825054574bfe2c0ae232e03e624fc377f6075f4 Mon Sep 17 00:00:00 2001 From: shijing Date: Thu, 23 Dec 2021 16:59:07 +0800 Subject: [PATCH 01/36] gantt¶meter --- hb_client/src/App.vue | 3 + .../components/Gantt/components/leftMenu.vue | 22 +++--- hb_client/src/components/Gantt/index.vue | 72 +++++++------------ hb_client/src/components/customForm/index.vue | 8 ++- .../src/components/customForm/review.vue | 8 ++- hb_client/src/views/pm/management.vue | 19 +++-- hb_client/src/views/pm/plan.vue | 4 +- hb_client/src/views/wpm/operationdo.vue | 26 ++++--- 8 files changed, 87 insertions(+), 75 deletions(-) diff --git a/hb_client/src/App.vue b/hb_client/src/App.vue index 14f48dc..1698488 100644 --- a/hb_client/src/App.vue +++ b/hb_client/src/App.vue @@ -60,4 +60,7 @@ export default { #customerForm .el-form-item{ margin-bottom: 30px!important; } + .overFlowShow .el-tabs__content{ + overflow: visible; + } diff --git a/hb_client/src/components/Gantt/components/leftMenu.vue b/hb_client/src/components/Gantt/components/leftMenu.vue index 2167e1c..60d0113 100644 --- a/hb_client/src/components/Gantt/components/leftMenu.vue +++ b/hb_client/src/components/Gantt/components/leftMenu.vue @@ -19,11 +19,19 @@ @expand-change="handlerExpand" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > - + - - + + + + + + + + @@ -44,19 +52,12 @@ export default { BGScrollTop: { handler: function(newValue) { let table = this.$refs.tableMenu.bodyWrapper; - // console.log(newValue, table); table.scrollTo(0, newValue); - // table.scrollTo({ - // top: newValue, - // left: 0, - // behavior: "smooth" - // }); } } }, data() { return { - // tableData: [], checkList: [], isShowHeaderBox: false, menuOpen: false, @@ -68,7 +69,6 @@ export default { handlerWatchScroll() { let table = this.$refs.tableMenu.bodyWrapper; table.addEventListener("scroll", e => { - // console.log(e.srcElement.scrollTop); this.$emit("TableScrollTop", e.srcElement.scrollTop); }); }, diff --git a/hb_client/src/components/Gantt/index.vue b/hb_client/src/components/Gantt/index.vue index d804356..68c38fd 100644 --- a/hb_client/src/components/Gantt/index.vue +++ b/hb_client/src/components/Gantt/index.vue @@ -1,6 +1,6 @@ @@ -138,6 +141,7 @@ export default { listLoading: true, proList: [], + }; }, computed: {}, @@ -183,9 +187,12 @@ export default { obj.planTime = [startTime, endTime]; obj.per = item.count; obj.type = 1; + obj.productName = item.product_.name; + obj.productNum = item.product_.specification; obj.isShow = true; arr.push(obj); } + debugger; that.proList = arr; }); } diff --git a/hb_client/src/views/pm/plan.vue b/hb_client/src/views/pm/plan.vue index 049681e..8e0e12e 100644 --- a/hb_client/src/views/pm/plan.vue +++ b/hb_client/src/views/pm/plan.vue @@ -87,7 +87,7 @@ @pagination="getplanList" /> - + - + @@ -603,6 +603,7 @@ export default { }, fieldList: { count: 0, + name:'', }, listQueryfield: { page: 1, @@ -796,7 +797,9 @@ export default { this.formID = scope.row.id; recordInit( this.formID).then((response) => { if (response.data) { + debugger; this.fieldList = response.data; + this.fieldList.name = response.data.form_.name; } }); this.dialogVisibleForm = true; @@ -818,16 +821,21 @@ export default { let _this = this; _this.record_data = []; //检查项目 this.fieldList.record_data.forEach((item) => { + if(item.field_type==='int'){ + item.field_value = parseInt(item.field_value) + }else if(item.field_type==='float'){ + item.field_value = parseFloat(item.field_value) + } _this.record_data.push({ id: item.id, - field_value: item.field_value, + field_value:item.field_value, }); }); this.record.record_data = _this.record_data; createRecord(this.formID, this.record).then((res) => { if (res.code >= 200) { this.getrecordLists(); - this.$message.success("创建成功!"); + this.$message.success("提交成功!"); this.dialogVisibleForm = false; } From dc5d7ff48e894d43e8b8f13df0e07ee8b7dc4877 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 23 Dec 2021 17:11:37 +0800 Subject: [PATCH 02/36] =?UTF-8?q?wf=20field=5Fdisplay=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wf/migrations/0020_auto_20211223_1006.py | 38 +++++++++++++++++++ hb_server/apps/wf/models.py | 22 +++++++---- hb_server/apps/wf/serializers.py | 24 ++++++++++++ hb_server/apps/wf/views.py | 2 +- hb_server/apps/wpm/serializers.py | 1 + hb_server/apps/wpm/views.py | 4 +- 6 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 hb_server/apps/wf/migrations/0020_auto_20211223_1006.py diff --git a/hb_server/apps/wf/migrations/0020_auto_20211223_1006.py b/hb_server/apps/wf/migrations/0020_auto_20211223_1006.py new file mode 100644 index 0000000..81df3fe --- /dev/null +++ b/hb_server/apps/wf/migrations/0020_auto_20211223_1006.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.9 on 2021-12-23 02:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0019_auto_20211221_0923'), + ] + + operations = [ + migrations.AddField( + model_name='customfield', + name='is_hidden', + field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的信息', verbose_name='是否隐藏'), + ), + migrations.AlterField( + model_name='customfield', + name='field_choice', + field=models.JSONField(blank=True, default=list, help_text='选项值,格式为list, 例["id":1, "name":"张三"]', verbose_name='选项值'), + ), + 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='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)', max_length=50, verbose_name='类型'), + ), + migrations.AlterField( + model_name='customfield', + name='label', + field=models.CharField(default='', help_text='处理特殊逻辑使用', max_length=1000, verbose_name='标签'), + ), + migrations.AlterField( + model_name='workflow', + name='title_template', + field=models.CharField(blank=True, default='{title}', help_text='工单字段的值可以作为参数写到模板中,格式如:你有一个待办工单:{title}', max_length=50, null=True, verbose_name='标题模板'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 94c9c44..27c2f4a 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -150,14 +150,16 @@ class CustomField(CommonAModel): ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), + ('cascader', '单选级联'), + ('cascaders', '多选级联'), + ('select_dg', '弹框单选'), + ('select_dgs', '弹框多选'), ('textarea', '文本域'), - ('selectuser', '单选用户'), - ('selectusers', '多选用户'), - ('file', '附件'), - ('draw', '绘图') + ('file', '附件') ) workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') - field_type = models.CharField('类型', max_length=50, choices=field_type_choices, help_text='5.字符串,10.整形,15.浮点型,20.布尔,25.日期,30.日期时间,35.单选框,40.多选框,45.下拉列表,50.多选下拉列表,55.文本域,60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)') + 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') 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顺序排列') @@ -167,9 +169,13 @@ class CustomField(CommonAModel): field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder') boolean_field_display = models.JSONField('布尔类型显示名', default=dict, blank=True, help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"是","0":"否"}或{"1":"需要","0":"不需要"},注意数字也需要引号') - field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True, - help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号') - label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签,json格式,调用方可根据标签自行处理特殊场景逻辑,loonflow只保存文本内容') + + field_choice = models.JSONField('选项值', default=list, blank=True, + help_text='选项值,格式为list, 例["id":1, "name":"张三"]') + + label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用') + + is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的信息') class Ticket(CommonBModel): """ diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index de756fe..0a38ac7 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -38,8 +38,12 @@ class TransitionSerializer(serializers.ModelSerializer): queryset = queryset.select_related('source_state','destination_state') return queryset +class FieldChoiceSerializer(serializers.Serializer): + id = serializers.Field(label='ID') + name = serializers.CharField(label='名称') class CustomFieldSerializer(serializers.ModelSerializer): + field_choice = FieldChoiceSerializer(label='选项列表', many=True) class Meta: model = CustomField fields = '__all__' @@ -107,8 +111,28 @@ class TicketDetailSerializer(serializers.ModelSerializer): key = i['field_key'] 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) return all_fields_l + def filter_display(self, item, field_value): + if item['id'] == field_value: + return + class TicketFlowSerializer(serializers.ModelSerializer): participant_ = UserSimpleSerializer(source='participant', read_only=True) state_ = StateSimpleSerializer(source='state', read_only=True) diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 2f9b092..06c3703 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -133,7 +133,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin return super().get_serializer_class() def filter_queryset(self, queryset): - if not self.request.query_params.get('category', None): + if not self.detail and not self.request.query_params.get('category', None): raise APIException('请指定查询分类') return super().filter_queryset(queryset) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 01c6315..034c1f8 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -382,6 +382,7 @@ class OperationMaterialCreate2Serailizer(serializers.ModelSerializer): class OperationMaterialCreate2ListSerailizer(serializers.ListSerializer): child=OperationMaterialCreate2Serailizer() + class OperationMaterialCreate3Serializer(serializers.ModelSerializer): material = serializers.PrimaryKeyRelatedField(required=True, queryset=Material.objects.all()) class Meta: diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 34556f6..8d906d2 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -343,7 +343,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet): vdata = serializer.validated_data if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK: pass - elif obj.step.process.id == 1: # 如果是冷加工可直接报废 + elif obj.step.process.id == 1 and \ + obj.act_state in [WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_TOTEST]: # 如果是冷加工可直接报废 if vdata.get('scrap_reason', None): obj.scrap_reason = vdata['scrap_reason'] else: @@ -375,6 +376,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): if workflow: exist_data = { 'wproduct':obj.id, + 'wproduct_number':obj.number, 'wproduct_name':obj.material.name, 'wproduct_specification':obj.material.specification, 'finder':request.user.id, From 866a0ecf484fc945654e55646ebf13b062250d00 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 08:53:05 +0800 Subject: [PATCH 03/36] customfield createupdate serializer --- hb_server/apps/wf/serializers.py | 9 ++++++++- hb_server/apps/wf/views.py | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 0a38ac7..8b1239c 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -43,11 +43,18 @@ class FieldChoiceSerializer(serializers.Serializer): name = serializers.CharField(label='名称') class CustomFieldSerializer(serializers.ModelSerializer): - field_choice = FieldChoiceSerializer(label='选项列表', many=True) class Meta: model = CustomField fields = '__all__' +class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer): + field_choice = FieldChoiceSerializer(label='选项列表', many=True) + class Meta: + model = CustomField + fields = ['workflow', 'field_type', 'field_key', 'field_name', + 'sort', 'default_value', 'description', 'placeholder', 'field_template', + 'boolean_field_display', 'field_choice', 'label', 'is_hidden'] + class TicketSimpleSerializer(serializers.ModelSerializer): class Meta: model = Ticket diff --git a/hb_server/apps/wf/views.py b/hb_server/apps/wf/views.py index 06c3703..02ac6a0 100644 --- a/hb_server/apps/wf/views.py +++ b/hb_server/apps/wf/views.py @@ -9,7 +9,7 @@ from django.core.exceptions import AppRegistryNotReady from rest_framework.response import Response from rest_framework import serializers from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin -from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer +from apps.wf.serializers import CustomFieldCreateUpdateSerializer, CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from django.shortcuts import get_object_or_404, render from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.decorators import action, api_view @@ -111,6 +111,11 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, filterset_fields = ['workflow', 'field_type'] ordering = ['sort'] + def get_serializer_class(self): + if self.action in ['create', 'update']: + return CustomFieldCreateUpdateSerializer + return super().get_serializer_class() + class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet): perms_map = {'*':'*'} queryset = Ticket.objects.all() From f8224373f3476a03ea7cd4df426e049b4260eec3 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 08:55:10 +0800 Subject: [PATCH 04/36] =?UTF-8?q?fifoitem=E5=A2=9E=E5=8A=A0fifo=5F=5Ftype?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 0956d4f..94ed73d 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -74,7 +74,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): perms_map = {'*': '*'} queryset = FIFOItem.objects.select_related('material', 'fifo').all() serializer_class = FIFOItemSerializer - filterset_fields = ['material', 'fifo'] + filterset_fields = ['material', 'fifo', 'fifo__type'] search_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] From b433476daf8eaa659e1b2e174920f3d44ccf5062 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 09:19:09 +0800 Subject: [PATCH 05/36] =?UTF-8?q?inm=20test=E6=8E=A5=E5=8F=A3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 94ed73d..279307f 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -99,13 +99,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): obj = serializer.save(create_by = self.request.user) tris = [] for m in record_data: # 保存记录详情 - form_field = m['form_field'] - m['field_name'] = form_field.field_name - m['field_key'] = form_field.field_key - m['field_type'] = form_field.field_type m['field_value'] = m['field_value'] - m['sort'] = form_field.sort - m['need_judge'] = form_field.need_judge m['is_testok'] = m['is_testok'] if 'is_testok' in m else True m['test_record'] = obj tris.append(TestRecordItem(**m)) From 32a1d9020de794963d28cb46ba10f96ec0d12ec4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 09:33:11 +0800 Subject: [PATCH 06/36] =?UTF-8?q?fifoitem=20=E5=A2=9E=E5=8A=A0is=5Ftested?= =?UTF-8?q?=20is=5Ftestok=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 279307f..42006dd 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -74,7 +74,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): perms_map = {'*': '*'} queryset = FIFOItem.objects.select_related('material', 'fifo').all() serializer_class = FIFOItemSerializer - filterset_fields = ['material', 'fifo', 'fifo__type'] + filterset_fields = ['material', 'fifo', 'fifo__type', 'is_tested', 'is_testok'] search_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] From f9a71a2fa40a471fd1915e23fc6150876c5746bf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 10:19:36 +0800 Subject: [PATCH 07/36] =?UTF-8?q?field=20choice=20=E9=9D=9E=E5=BF=85?= =?UTF-8?q?=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 8b1239c..7740acf 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -48,7 +48,7 @@ class CustomFieldSerializer(serializers.ModelSerializer): fields = '__all__' class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer): - field_choice = FieldChoiceSerializer(label='选项列表', many=True) + field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False) class Meta: model = CustomField fields = ['workflow', 'field_type', 'field_key', 'field_name', From 78aea3172584244bbca44633dddcb9eb4ca37f2e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 13:49:52 +0800 Subject: [PATCH 08/36] field choice serializer write only --- hb_server/apps/wf/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 7740acf..5dba5df 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -48,7 +48,7 @@ class CustomFieldSerializer(serializers.ModelSerializer): fields = '__all__' class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer): - field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False) + field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False, write_only=True) class Meta: model = CustomField fields = ['workflow', 'field_type', 'field_key', 'field_name', From a5062a375ef33b73ba08386336c640be2df73534 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 13:52:18 +0800 Subject: [PATCH 09/36] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E8=A1=A8selled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py index e1e9f1a..8937211 100644 --- a/hb_server/apps/inm/models.py +++ b/hb_server/apps/inm/models.py @@ -92,7 +92,8 @@ class IProduct(BaseModel): material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') batch = models.CharField('所属批次号', max_length=100, default='') - wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) + wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, + null=True, blank=True, related_name='iproduct_wproduct') is_mtested = models.BooleanField('是否军检', default=False) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True) @@ -103,7 +104,8 @@ class FIFOItemProduct(BaseModel): 出入库产品 """ fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE) - wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) + wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True, + related_name='fifoitem_wproduct') number = models.CharField('物品编号', max_length=50) material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) iproduct = models.ForeignKey(IProduct, verbose_name='关联库存产品', null=True, blank=True, on_delete=models.SET_NULL) From 7c073a54cbfd8755821c356963f3edefe0cc1853 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 14:25:56 +0800 Subject: [PATCH 10/36] to_internal value fieldchoice bug --- hb_server/apps/wf/models.py | 6 +++--- hb_server/apps/wf/serializers.py | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 27c2f4a..35fc069 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -164,9 +164,9 @@ class CustomField(CommonAModel): field_name = models.CharField('字段名称', max_length=50) sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列') default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时,可以将此内容作为表单中的该字段的默认值') - description = models.CharField('描述', max_length=100, blank=True, default='', help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述') - placeholder = models.CharField('占位符', max_length=100, blank=True, default='', help_text='用户工单详情表单中作为字段的占位符显示') - field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder') + description = models.CharField('描述', max_length=100, blank=True, null=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述') + placeholder = models.CharField('占位符', max_length=100, blank=True, null=True, help_text='用户工单详情表单中作为字段的占位符显示') + field_template = models.TextField('文本域模板', null=True, blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder') boolean_field_display = models.JSONField('布尔类型显示名', default=dict, blank=True, help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"是","0":"否"}或{"1":"需要","0":"不需要"},注意数字也需要引号') diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index 5dba5df..f3d055d 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -42,18 +42,24 @@ class FieldChoiceSerializer(serializers.Serializer): id = serializers.Field(label='ID') name = serializers.CharField(label='名称') + def to_internal_value(self, data): + return data + + class CustomFieldSerializer(serializers.ModelSerializer): class Meta: model = CustomField fields = '__all__' class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer): - field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False, write_only=True) + + field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False) class Meta: model = CustomField fields = ['workflow', 'field_type', 'field_key', 'field_name', 'sort', 'default_value', 'description', 'placeholder', 'field_template', 'boolean_field_display', 'field_choice', 'label', 'is_hidden'] + class TicketSimpleSerializer(serializers.ModelSerializer): class Meta: From 6682d43022a7f3a8c240dd2446f569f5ed56ca76 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 24 Dec 2021 14:37:59 +0800 Subject: [PATCH 11/36] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89allfield?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wf/serializers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/wf/serializers.py b/hb_server/apps/wf/serializers.py index f3d055d..6daa326 100644 --- a/hb_server/apps/wf/serializers.py +++ b/hb_server/apps/wf/serializers.py @@ -38,13 +38,17 @@ class TransitionSerializer(serializers.ModelSerializer): queryset = queryset.select_related('source_state','destination_state') return queryset -class FieldChoiceSerializer(serializers.Serializer): - id = serializers.Field(label='ID') - name = serializers.CharField(label='名称') +class AllField(serializers.Field): + def to_representation(self, value): + return value def to_internal_value(self, data): return data +class FieldChoiceSerializer(serializers.Serializer): + id = AllField(label='ID') + name = serializers.CharField(label='名称') + class CustomFieldSerializer(serializers.ModelSerializer): class Meta: From 811fe24fc78533aa61e413dc30b24b245eaf509d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 09:45:23 +0800 Subject: [PATCH 12/36] =?UTF-8?q?=E8=BF=94=E5=B7=A5=E8=BF=94=E4=BF=AE?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E6=93=8D=E4=BD=9C=E5=86=B3=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/sam/views.py | 6 +++ .../wf/migrations/0021_auto_20211224_1426.py | 33 ++++++++++++ hb_server/apps/wf/models.py | 4 +- hb_server/apps/wpm/models.py | 30 ++++++++++- hb_server/apps/wpm/services.py | 7 +-- hb_server/apps/wpm/signals.py | 52 ++++++++++++++----- hb_server/apps/wpm/views.py | 16 +++--- 7 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 hb_server/apps/wf/migrations/0021_auto_20211224_1426.py diff --git a/hb_server/apps/sam/views.py b/hb_server/apps/sam/views.py index 10a6d97..8a5f004 100644 --- a/hb_server/apps/sam/views.py +++ b/hb_server/apps/sam/views.py @@ -14,6 +14,8 @@ from rest_framework.decorators import action from django.db.models import F from rest_framework.response import Response from django.utils import timezone + +from apps.wf.models import Workflow # Create your views here. class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet): """ @@ -168,6 +170,10 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C FIFOItemProduct.objects.bulk_create(ipxs) # 更新成品库情况 ips.update(is_saled=True) + # 更新动态产品表情况 + from apps.wpm.models import WProduct + WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update( + act_state=WProduct.WPR_ACT_STATE_SELLED) # 更新库存 update_inm(fifo) # 变更审核状态 diff --git a/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py b/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py new file mode 100644 index 0000000..8013a5c --- /dev/null +++ b/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.9 on 2021-12-24 06:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0020_auto_20211223_1006'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='description', + field=models.CharField(blank=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述', max_length=100, null=True, verbose_name='描述'), + ), + migrations.AlterField( + model_name='customfield', + name='field_template', + field=models.TextField(blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder', null=True, verbose_name='文本域模板'), + ), + 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, cascader, cascaders,textarea, file', max_length=50, verbose_name='类型'), + ), + migrations.AlterField( + model_name='customfield', + name='placeholder', + field=models.CharField(blank=True, help_text='用户工单详情表单中作为字段的占位符显示', max_length=100, null=True, verbose_name='占位符'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 35fc069..36a1cb3 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -174,8 +174,8 @@ class CustomField(CommonAModel): help_text='选项值,格式为list, 例["id":1, "name":"张三"]') label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用') - - is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的信息') + # hook = models.CharField('hook', max_length=1000, default='', help_text='获取下拉选项用于动态选项值') + is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的字段信息') class Ticket(CommonBModel): """ diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 169e295..55491ca 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -36,6 +36,7 @@ class WProduct(CommonAModel): WPR_ACT_STATE_NOTOK = 50 WPR_ACT_STATE_TOFINALTEST = 60 WPR_ACT_STATE_SCRAP = 70 + WPR_ACT_STATE_SELLED = 80 act_state_choices=( (WPR_ACT_STATE_TORETEST, '待复检'), (WPR_ACT_STATE_DOWAIT, '操作准备中'), @@ -46,7 +47,8 @@ class WProduct(CommonAModel): (WPR_ACT_STATE_INM, '已入库'), (WPR_ACT_STATE_NOTOK, '不合格'), (WPR_ACT_STATE_TOFINALTEST, '待成品检验'), - (WPR_ACT_STATE_SCRAP, '已报废') + (WPR_ACT_STATE_SCRAP, '已报废'), + (WPR_ACT_STATE_SELLED, '已售出'), ) SCRAP_REASON_QIPAO = 10 SCRAP_REASON_PODIAN = 20 @@ -58,6 +60,26 @@ class WProduct(CommonAModel): (30, '划伤'), (40, '其他') ) + + NG_BACK_WORK = 10 + NG_BACK_FIX = 20 + NG_SCRAP = 30 + NG_ACCEPT = 40 + NG_PERMIT = 50 + NG_DOWN = 60 + NG_BACK_FROM = 70 + NG_RECALL = 80 + + ng_choices = ( + (NG_BACK_WORK, '返工'), + (NG_BACK_FIX, '返修'), + (NG_SCRAP, '报废'), + (NG_ACCEPT, '让步接收'), + (NG_PERMIT, '偏离许可'), + (NG_DOWN, '降级使用'), + (NG_BACK_FROM, '退回供方'), + (NG_RECALL, '召回') + ) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step') @@ -69,6 +91,9 @@ class WProduct(CommonAModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan') scrap_reason = models.IntegerField('报废原因', choices=scrap_reason_choices, null=True, blank=True) + + ng_sign = models.PositiveSmallIntegerField('不合格标记', choices=ng_choices, null=True, blank=True) + warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True) operation = models.ForeignKey('wpm.operation', verbose_name='当前操作', on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation') @@ -89,6 +114,7 @@ class WprouctTicket(CommonAModel): """ 玻璃审批工单 """ + number = models.CharField('物品编号', null=True, blank=True, max_length=50) wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE) @@ -96,7 +122,7 @@ class WprouctTicket(CommonAModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE) ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket') - decision = models.CharField('最终决定', null=True, blank=True, max_length=100) + decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True) class Pick(CommonADModel): """ diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 9d56be0..30ba1f2 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -46,17 +46,18 @@ class WpmServies(object): if is_testok: if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检 wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT + wproduct.ng_sign = None # 把不合格标记去除 elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验 wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST else: wproduct.act_state = WProduct.WPR_ACT_STATE_OK if wproduct.number is None: # 产生半成品编号 wproduct.number = 'WP'+ranstr(7) - # 更新子计划状态 - # 更新子计划主产品数 + + # 更新子计划合格进度 instance = 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.count_ok = instance.count_ok + 1 instance.save() else:# 如果不合格 wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK diff --git a/hb_server/apps/wpm/signals.py b/hb_server/apps/wpm/signals.py index 2c816a2..3d27b38 100644 --- a/hb_server/apps/wpm/signals.py +++ b/hb_server/apps/wpm/signals.py @@ -1,9 +1,12 @@ from django.db.models.signals import post_save +from apps.mtm.models import SubprodctionMaterial +from apps.pm.models import SubProductionProgress from apps.qm.models import TestRecord from apps.wf.models import Ticket from django.dispatch import receiver - +from rest_framework import exceptions from apps.wpm.models import WProduct, WprouctTicket +from apps.wpm.models import OperationWproduct @receiver(post_save, sender=Ticket) @@ -43,18 +46,41 @@ def handleTicket(sender, instance, created, **kwargs): ticket_data = instance.ticket_data wt = instance.wt_ticket wp = wt.wproduct + decision = WProduct.NG_BACK_WORK + if 'decision_1' in ticket_data and ticket_data['decision_1']: - wt.decision = ticket_data['decision_1'] - if ticket_data['decision_1'] in ['返工', '返修']: - pass - elif ticket_data['decision_1'] in ['让步接收']: - wp.act_state = WProduct.WPR_ACT_STATE_OK + decision = ticket_data['decision_1'] elif 'decision_2' in ticket_data and ticket_data['decision_2']: - wp.decision = ticket_data['decision_2'] - if ticket_data['decision_2'] in ['返工', '返修']: - pass - elif ticket_data['decision_2'] in ['让步接收']: - wp.act_state = WProduct.WPR_ACT_STATE_OK - wt.save() - wp.save() + decision = ticket_data['decision_2'] + + + wp.ng_sign = decision + if decision in [WProduct.NG_BACK_WORK, WProduct.NG_BACK_FIX]: + step = ticket_data['back_step'] + wp.step = step + # 找到当时所属的计划 + sp = OperationWproduct.objects.filter(operation__is_submited=True, operation__step=step).first() + if sp: + wp.subproduction_plan = sp + wt.save() + wp.ticket = None # 解除当前工单 + 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() + + else: + raise exceptions.APIException('返回步骤点错误') + + elif decision in [WProduct.NG_ACCEPT, WProduct.NG_PERMIT]: + wp.act_state = WProduct.WPR_ACT_STATE_OK + wp.ng_sign = decision + wt.save() + wp.ticket = None # 解除当前工单 + wp.save() + + \ No newline at end of file diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 8d906d2..ff06c97 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -370,8 +370,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 发起不合格审理单 """ obj = self.get_object() - if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK: - raise exceptions.APIException('非检验不合格产品不可发起不合格审理') + if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None: + raise exceptions.APIException('该产品不可发起不合格审理') workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first() if workflow: exist_data = { @@ -539,11 +539,13 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd else: wp.act_state = WProduct.WPR_ACT_STATE_TOTEST wp.material = wsp.main_product - # 更新子计划进度 - instance = SubProductionProgress.objects.get(subproduction_plan=wsp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 - instance.save() + # 更新子计划生产进度 + # 如果产品有返工标记不做计算 + if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: + instance = SubProductionProgress.objects.get(subproduction_plan=wsp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 + instance.save() wp.operation = None wp.update_by = request.user wp.save() From 6a1641115f59f72d6f26646fb17817734153f3c7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 09:49:23 +0800 Subject: [PATCH 13/36] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inm/migrations/0024_auto_20211227_0948.py | 25 +++++++++++++++++ .../0022_alter_customfield_is_hidden.py | 18 ++++++++++++ .../wpm/migrations/0038_auto_20211227_0948.py | 28 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 hb_server/apps/inm/migrations/0024_auto_20211227_0948.py create mode 100644 hb_server/apps/wf/migrations/0022_alter_customfield_is_hidden.py create mode 100644 hb_server/apps/wpm/migrations/0038_auto_20211227_0948.py diff --git a/hb_server/apps/inm/migrations/0024_auto_20211227_0948.py b/hb_server/apps/inm/migrations/0024_auto_20211227_0948.py new file mode 100644 index 0000000..16c3b5f --- /dev/null +++ b/hb_server/apps/inm/migrations/0024_auto_20211227_0948.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.9 on 2021-12-27 01:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0038_auto_20211227_0948'), + ('inm', '0023_auto_20211216_0945'), + ] + + operations = [ + migrations.AlterField( + model_name='fifoitemproduct', + name='wproduct', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fifoitem_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'), + ), + migrations.AlterField( + model_name='iproduct', + name='wproduct', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='iproduct_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'), + ), + ] diff --git a/hb_server/apps/wf/migrations/0022_alter_customfield_is_hidden.py b/hb_server/apps/wf/migrations/0022_alter_customfield_is_hidden.py new file mode 100644 index 0000000..c197db2 --- /dev/null +++ b/hb_server/apps/wf/migrations/0022_alter_customfield_is_hidden.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2021-12-27 01:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0021_auto_20211224_1426'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='is_hidden', + field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的字段信息', verbose_name='是否隐藏'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0038_auto_20211227_0948.py b/hb_server/apps/wpm/migrations/0038_auto_20211227_0948.py new file mode 100644 index 0000000..d419bcc --- /dev/null +++ b/hb_server/apps/wpm/migrations/0038_auto_20211227_0948.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.9 on 2021-12-27 01:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0037_wproduct_scrap_reason'), + ] + + operations = [ + migrations.AddField( + model_name='wproduct', + name='ng_sign', + field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='不合格标记'), + ), + migrations.AlterField( + model_name='wproduct', + name='act_state', + field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态'), + ), + migrations.AlterField( + model_name='wprouctticket', + name='decision', + field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='最终决定'), + ), + ] From 71a0858126bd04c781edc7348689e6cc6f52b8a9 Mon Sep 17 00:00:00 2001 From: shijing Date: Mon, 27 Dec 2021 10:11:11 +0800 Subject: [PATCH 14/36] retrial --- hb_client/src/views/workflow/customfield.vue | 486 ++++++++++--------- hb_client/src/views/workflow/state.vue | 1 + hb_client/src/views/workflow/ticket.vue | 12 +- hb_client/src/views/wpm/need.vue | 203 +++++--- 4 files changed, 401 insertions(+), 301 deletions(-) diff --git a/hb_client/src/views/workflow/customfield.vue b/hb_client/src/views/workflow/customfield.vue index 44fbdac..8013b84 100644 --- a/hb_client/src/views/workflow/customfield.vue +++ b/hb_client/src/views/workflow/customfield.vue @@ -1,12 +1,11 @@ @@ -67,18 +68,18 @@ label-position="right" :rules="rule1"> - + - + - + - + - + + + + 添加 - - - + + + + + + + - + - - + + - - + + - - + + + + + + 显示 + 隐藏 +
@@ -120,39 +146,53 @@
diff --git a/hb_client/src/views/workflow/state.vue b/hb_client/src/views/workflow/state.vue index ce5e364..e1b30e1 100644 --- a/hb_client/src/views/workflow/state.vue +++ b/hb_client/src/views/workflow/state.vue @@ -187,6 +187,7 @@ + diff --git a/hb_client/src/views/workflow/ticket.vue b/hb_client/src/views/workflow/ticket.vue index bb93ffd..fc50bc4 100644 --- a/hb_client/src/views/workflow/ticket.vue +++ b/hb_client/src/views/workflow/ticket.vue @@ -308,9 +308,9 @@ @@ -319,9 +319,9 @@ diff --git a/hb_client/src/views/wpm/need.vue b/hb_client/src/views/wpm/need.vue index 6eda955..c1851ad 100644 --- a/hb_client/src/views/wpm/need.vue +++ b/hb_client/src/views/wpm/need.vue @@ -508,98 +508,78 @@ - - - - - - - - - - - - + + + + + + + + + + + + From 6afd62dc93ec28ed8e51a44e895baa9b5bc7e3e1 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 10:59:40 +0800 Subject: [PATCH 15/36] wf ticket detail bug --- hb_server/apps/pm/models.py | 1 + hb_server/apps/pm/services.py | 10 ++++++++++ hb_server/apps/pm/signals.py | 2 ++ hb_server/apps/wf/models.py | 4 ++-- hb_server/apps/wf/serializers.py | 31 ++++++++++++++++--------------- 5 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 hb_server/apps/pm/services.py diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 7588491..539d523 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=list, null=True, blank=True) is_planed = models.BooleanField('是否已排产', default=False) class Meta: verbose_name = '生产计划' diff --git a/hb_server/apps/pm/services.py b/hb_server/apps/pm/services.py new file mode 100644 index 0000000..c308f1f --- /dev/null +++ b/hb_server/apps/pm/services.py @@ -0,0 +1,10 @@ +from apps.pm.models import ProductionPlan, SubProductionPlan +class PmService: + + @classmethod + def update_plan_process_json(cls, plan:ProductionPlan): + """ + 更新计划统计字段 + """ + ret = {} + subplans = SubProductionPlan.objects.filter() \ No newline at end of file diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py index c4871ee..bed7671 100644 --- a/hb_server/apps/pm/signals.py +++ b/hb_server/apps/pm/signals.py @@ -26,6 +26,8 @@ def update_subplan_main(sender, instance, created, **kwargs): plan.count_real = subplan.main_count_real plan.count_ok = subplan.main_count_ok plan.save() + + 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): From 7713cc045d17f084542b578b28bcf1f2d623ab67 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 13:18:30 +0800 Subject: [PATCH 16/36] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0018_productionplan_process_json.py | 18 +++++++++++++++ .../wf/migrations/0023_auto_20211227_1318.py | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 hb_server/apps/pm/migrations/0018_productionplan_process_json.py create mode 100644 hb_server/apps/wf/migrations/0023_auto_20211227_1318.py 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/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='标签'), + ), + ] From c8c9cd45541e00e99bbfb20b0fe2b79ee09fe775 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 14:23:05 +0800 Subject: [PATCH 17/36] =?UTF-8?q?=E5=8F=91=E8=B5=B7=E4=B8=8D=E5=90=88?= =?UTF-8?q?=E6=A0=BC=E5=AE=A1=E7=90=86=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/services.py | 9 +++++---- hb_server/apps/wpm/signals.py | 17 +++++++++-------- hb_server/apps/wpm/views.py | 10 +++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 30ba1f2..8dcde40 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,10 +57,9 @@ class WpmServies(object): wproduct.number = 'WP'+ranstr(7) # 更新子计划合格进度 - instance = 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() + SubProductionProgress.objects.filter(subproduction_plan=wproduct.subproduction_plan, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_ok=F('count_ok')+1) + else:# 如果不合格 wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK # 需要走不合格品审理的工单 diff --git a/hb_server/apps/wpm/signals.py b/hb_server/apps/wpm/signals.py index 3d27b38..e7e07e4 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,9 @@ 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: + SubProductionProgress.objects.filter(subproduction_plan=sp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_ok=F('count_ok')-1) else: raise exceptions.APIException('返回步骤点错误') diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index ff06c97..de6ca2c 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,8 @@ 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, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 - instance.save() + SubProductionProgress.objects.filter(subproduction_plan=wsp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_real=F('count_real')+1) wp.operation = None wp.update_by = request.user wp.save() From 15812bf04816eafdd5b3b37c923890c5c6629488 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 14:56:55 +0800 Subject: [PATCH 18/36] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E6=8C=89=E5=B7=A5=E5=BA=8F=E5=AD=97=E6=AE=B5=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/pm/models.py | 2 +- hb_server/apps/pm/services.py | 21 +++++++++++++++++++-- hb_server/apps/pm/signals.py | 3 +++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 539d523..d9bf331 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -29,7 +29,7 @@ class ProductionPlan(CommonAModel): count_ok = models.PositiveIntegerField('合格数', default=0) start_date = models.DateField('计划开工日期') end_date = models.DateField('计划完工日期') - process_json = models.JSONField('按工序的统计数', default=list, null=True, blank=True) + 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/services.py b/hb_server/apps/pm/services.py index c308f1f..abcf581 100644 --- a/hb_server/apps/pm/services.py +++ b/hb_server/apps/pm/services.py @@ -1,10 +1,27 @@ +from django.db.models.aggregates import Sum from apps.pm.models import ProductionPlan, SubProductionPlan +import math class PmService: @classmethod def update_plan_process_json(cls, plan:ProductionPlan): """ - 更新计划统计字段 + 更新计划按工序统计字段 """ ret = {} - subplans = SubProductionPlan.objects.filter() \ No newline at end of file + 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 bed7671..b3cc7bb 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): @@ -26,6 +27,8 @@ 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) From 756f2efc5661009e064024f117a1cc57245fb899 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 15:49:32 +0800 Subject: [PATCH 19/36] =?UTF-8?q?wpm=20update=20=E4=B8=8D=E8=A7=A6?= =?UTF-8?q?=E5=8F=91singal/=20pm=20process=5Fjson=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/pm/serializers.py | 13 ++++++++----- hb_server/apps/pm/services.py | 2 +- hb_server/apps/pm/signals.py | 3 ++- hb_server/apps/wpm/services.py | 9 +++++---- hb_server/apps/wpm/signals.py | 6 ++++-- hb_server/apps/wpm/views.py | 6 ++++-- 6 files changed, 24 insertions(+), 15 deletions(-) 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 index abcf581..a3771c9 100644 --- a/hb_server/apps/pm/services.py +++ b/hb_server/apps/pm/services.py @@ -1,6 +1,6 @@ from django.db.models.aggregates import Sum from apps.pm.models import ProductionPlan, SubProductionPlan -import math + class PmService: @classmethod diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py index b3cc7bb..afd8411 100644 --- a/hb_server/apps/pm/signals.py +++ b/hb_server/apps/pm/signals.py @@ -14,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: @@ -27,7 +28,7 @@ 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/wpm/services.py b/hb_server/apps/wpm/services.py index 8dcde40..04b8e87 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -57,12 +57,13 @@ class WpmServies(object): wproduct.number = 'WP'+ranstr(7) # 更新子计划合格进度 - SubProductionProgress.objects.filter(subproduction_plan=wproduct.subproduction_plan, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_ok=F('count_ok')+1) - + ins = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + 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 e7e07e4..6c61165 100644 --- a/hb_server/apps/wpm/signals.py +++ b/hb_server/apps/wpm/signals.py @@ -70,8 +70,10 @@ def handleTicket(sender, instance, created, **kwargs): wp.save() # 更新子计划合格进度 if sp != wt.subproduction_plan: - SubProductionProgress.objects.filter(subproduction_plan=sp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_ok=F('count_ok')-1) + 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 de6ca2c..8cfb65c 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -544,8 +544,10 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 更新子计划生产进度 # 如果产品有返工标记不做计算 if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: - SubProductionProgress.objects.filter(subproduction_plan=wsp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).update(count_real=F('count_real')+1) + ins = SubProductionProgress.objects.filter(subproduction_plan=wsp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + ins.count_real = ins.count_real + 1 + ins.save() wp.operation = None wp.update_by = request.user wp.save() From c9219ea495272265bcf14c3970b4eeb1f0ca84d8 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 15:52:05 +0800 Subject: [PATCH 20/36] =?UTF-8?q?bug=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 8cfb65c..a3b840c 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -544,7 +544,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 更新子计划生产进度 # 如果产品有返工标记不做计算 if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: - ins = SubProductionProgress.objects.filter(subproduction_plan=wsp, + ins = SubProductionProgress.objects.get(subproduction_plan=wsp, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) ins.count_real = ins.count_real + 1 ins.save() From 847e77656808f785552563fe4de98b580abe5d50 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 27 Dec 2021 15:53:32 +0800 Subject: [PATCH 21/36] update_plan_process_json bug --- hb_server/apps/pm/services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/pm/services.py b/hb_server/apps/pm/services.py index a3771c9..0b6894d 100644 --- a/hb_server/apps/pm/services.py +++ b/hb_server/apps/pm/services.py @@ -10,11 +10,11 @@ class PmService: """ ret = {} subplans = SubProductionPlan.objects.filter(production_plan=plan, is_deleted=False) - qs = subplans.values('process', 'process__name', 'process_number').annotate(count=Sum('main_count'), + 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']] = { + ret[i['process__number']] = { 'count':i['count'], 'count_real':i['count_real'], 'count_ok':i['count_ok'], From 90f02fd7bb28960adc0b760876d079df33f2fb7b Mon Sep 17 00:00:00 2001 From: shijing Date: Mon, 27 Dec 2021 17:09:56 +0800 Subject: [PATCH 22/36] sssss --- hb_client/src/api/workflow.js | 7 + hb_client/src/components/Gantt/index.vue | 41 ++- hb_client/src/router/index.js | 8 - hb_client/src/views/pm/gantt.vue | 56 +-- hb_client/src/views/workflow/ticketHandle.vue | 328 +++++++++--------- hb_client/src/views/wpm/need.vue | 31 +- 6 files changed, 246 insertions(+), 225 deletions(-) diff --git a/hb_client/src/api/workflow.js b/hb_client/src/api/workflow.js index 001df7e..1b6189c 100644 --- a/hb_client/src/api/workflow.js +++ b/hb_client/src/api/workflow.js @@ -240,3 +240,10 @@ export function getCodes() { method: 'get' }) } +//工单详情 +export function getWorkflowInit(id) { + return request({ + url: `/wf/workflow/${id}/init/`, + method: 'get' + }) +} diff --git a/hb_client/src/components/Gantt/index.vue b/hb_client/src/components/Gantt/index.vue index 68c38fd..a972888 100644 --- a/hb_client/src/components/Gantt/index.vue +++ b/hb_client/src/components/Gantt/index.vue @@ -201,14 +201,6 @@ width: item.widthMe + 'px', top: item.top + 'px' }" - @mouseover=" - lineMouseover( - `line${item.id}`, - $event, - item.id, - item.parentId, - index - )" @mouseleave="lineMouseleave" @mouseenter=" lineMouseenter( @@ -303,6 +295,8 @@ }, data() { return { + windowWidth:0, + windowHeight:0, leftYear:2021, timeRange: [], disable: true, @@ -374,6 +368,13 @@ }; }, mounted(){ + if(window.innerHeight){ + this.windowWidth = window.innerWidth; + this.windowHeight = window.innerHeight; + }else{ + this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight; + this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth; + } let currentYear = new Date().getFullYear(); this.timeRange = [currentYear+'-01',currentYear+'-12']; this.list = [...this.proList]; @@ -518,10 +519,8 @@ }, //修改后续高度 resetTop(zindex, reduce, isexpand) { - // console.log(zindex, reduce, isexpand); let num = reduce ? -40 : 40; if (!reduce && !isexpand) { - // console.log(2); this.list.forEach((item, index) => { if (index > zindex) { item.top = item.top + 40; @@ -533,7 +532,6 @@ } }); } else { - // console.log(1); this.list.forEach((item, index) => { if (index > zindex) { item.top = item.top + num * this.list[zindex].children.length; @@ -584,7 +582,6 @@ let widthMe = width - left; parent.widthMe = parent.widthChild = widthMe; parent.left = left; - // return parent; }, // 转为分组 handlerGroup(row) { @@ -658,10 +655,8 @@ let index = this.list.findIndex(k => { return k.id == parentId; }); - // console.log(parentId); this.list.forEach(item => { if (item.id == parentId) { - // console.log(item, this.list); item.children.forEach(k => { if (k.id == id) { k.per = per; @@ -813,6 +808,17 @@ Math.round(end / this.currentDaySize.value) * this.currentDaySize.value - this.currentDaySize.value; + let infoDomX=0,infoDomY = 0; + if(this.windowWidth-e.clientX>230){ + infoDomX = e.clientX; + }else{ + infoDomX = this.windowWidth-230; + } + if(this.windowHeight-e.clientY>200){ + infoDomY = e.clientY; + }else{ + infoDomY = this.windowHeight-200; + } this.currentProjectMsg = { name: this.computedList[index].name, allTime: (end - start) / this.currentDaySize.value + 1, @@ -820,8 +826,8 @@ per1: this.computedList[index].per1, startTime: this.computedWithTime(start), endTime: this.computedWithTime(end), - left: e.clientX, - top: e.clientY + 20 + left:infoDomX , + top: infoDomY }; /* * left: @@ -848,7 +854,6 @@ left: 0, top: 0 }; - // this.handlerSelect(); return; } this.currentLineDay = { @@ -1029,7 +1034,6 @@ }); } }, - beforeDestroy() { window.removeEventListener("scroll", this.handleScroll); document.onmousemove = document.onmouseup = null; @@ -1107,6 +1111,7 @@ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); border-radius: 4px; border: 1px solid #ebeef5; + pointer-events: none; .lineMsg { margin-bottom: 10px; diff --git a/hb_client/src/router/index.js b/hb_client/src/router/index.js index 3ac6882..7f13757 100644 --- a/hb_client/src/router/index.js +++ b/hb_client/src/router/index.js @@ -185,14 +185,6 @@ export const asyncRoutes = [ component: () => import('@/views/pm/management'), meta: { title: '生产任务管理', icon: 'example', perms: ['pm_resources'] } } - , - { - path: 'gantt', - name: 'gantt', - component: () => import('@/views/pm/gantt'), - meta: { title: '甘特图', icon: 'example',perms: ['vendor_manage'] }, - // hidden: true - } ] } , diff --git a/hb_client/src/views/pm/gantt.vue b/hb_client/src/views/pm/gantt.vue index 6c2b376..079d0a4 100644 --- a/hb_client/src/views/pm/gantt.vue +++ b/hb_client/src/views/pm/gantt.vue @@ -300,6 +300,8 @@ }, data() { return { + windowWidth:0, + windowHeight:0, timeRange:[], disable: true, //当前项是否是子集 @@ -499,11 +501,6 @@ TableScrollTop(val) { let lineBG = this.$refs.lineBG; lineBG.scrollTo(0, val); - // lineBG.scrollTo({ - // top: val, - // left: 0, - // behavior: "smooth" - // }); }, handlerBGScroll(e) { this.BGScrollTop = this.$refs.chart.scrollTop; @@ -862,15 +859,9 @@ ) * this.currentDaySize.value + this.currentDaySize.value; - let end = - parseInt(this.$refs[dom][0].style.left) + - parseInt(this.$refs[dom][0].style.width); - end = - Math.round(end / this.currentDaySize.value) * this.currentDaySize.value; - this.currentLineDay = { - start, - end - }; + let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width); + end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value; + this.currentLineDay = { start, end }; this.isHover = true; this.handlerSelect(this.computedList[index]); this.lineMouseenter(dom, e, id, parentId, index); @@ -888,13 +879,21 @@ Math.round( parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value ) * this.currentDaySize.value; - let end = - parseInt(this.$refs[dom][0].style.left) + - parseInt(this.$refs[dom][0].style.width); - end = - Math.round(end / this.currentDaySize.value) * - this.currentDaySize.value - - this.currentDaySize.value; + let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width); + end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value - this.currentDaySize.value; + debugger; + let infoDomX=0,infoDomY = 0; + if(this.windowWidth-e.clientX>230){ + infoDomX = e.clientX; + }else{ + infoDomX = this.windowWidth-230; + } + if(this.windowHeight-e.clientY>200){ + infoDomY = e.clientY; + }else{ + infoDomY = this.windowHeight-200; + } + // if(){}else{} this.currentProjectMsg = { name: this.computedList[index].name, allTime: (end - start) / this.currentDaySize.value + 1, @@ -902,8 +901,8 @@ per1: this.computedList[index].per1, startTime: this.computedWithTime(start), endTime: this.computedWithTime(end), - left: e.clientX , - top: e.clientY + 20 + left:infoDomX , + top: infoDomY }; /* * left: @@ -1095,6 +1094,10 @@ } }, watch: { + detailInfo(e,data) { + showDiv.style.left = (event.pageX - 300) + 'px'; + showDiv.style.top = (event.pageY - 120) + 'px'; + }, currentDaySize(newValue, oldValue) { this.list.forEach(item => { item.left = (item.left / oldValue.value) * newValue.value; @@ -1111,6 +1114,13 @@ } }, mounted() { + if(window.innerHeight){ + this.windowWidth = window.innerWidth; + this.windowHeight = window.innerHeight; + }else{ + this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight; + this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth; + } document.addEventListener("scroll", this.handleScroll); this.getDay(); this.setStoneLine(); diff --git a/hb_client/src/views/workflow/ticketHandle.vue b/hb_client/src/views/workflow/ticketHandle.vue index 8aed872..85ef7ae 100644 --- a/hb_client/src/views/workflow/ticketHandle.vue +++ b/hb_client/src/views/workflow/ticketHandle.vue @@ -35,7 +35,7 @@ - + {{ticketDetail.ticket_data[item.field_key]}} @@ -73,9 +73,9 @@ @@ -84,9 +84,9 @@ @@ -205,6 +205,7 @@ diff --git a/hb_client/src/views/wpm/productjy.vue b/hb_client/src/views/wpm/productjy.vue index 638bb83..5126f4c 100644 --- a/hb_client/src/views/wpm/productjy.vue +++ b/hb_client/src/views/wpm/productjy.vue @@ -1,114 +1,241 @@ From 8563a0c7f92db20084ef455efca26a192c2193a7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Dec 2021 09:17:04 +0800 Subject: [PATCH 32/36] =?UTF-8?q?pm=E7=9B=B8=E5=85=B3=E8=A1=A8=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0count=5Fnotok=E5=AD=97=E6=AE=B5=EF=BC=8C=20update=5Fwp?= =?UTF-8?q?roduct=5Flog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/develop/apps.py | 4 +- hb_server/apps/develop/migrations/__init__.py | 0 hb_server/apps/develop/models.py | 3 ++ hb_server/apps/develop/tests.py | 3 ++ hb_server/apps/develop/urls.py | 10 ++++ hb_server/apps/develop/views.py | 22 ++++++--- .../pm/migrations/0020_auto_20211229_0912.py | 48 +++++++++++++++++++ hb_server/apps/pm/models.py | 11 +++-- hb_server/apps/pm/signals.py | 2 + hb_server/apps/wpm/services.py | 17 ++++--- hb_server/apps/wpm/signals.py | 1 + hb_server/apps/wpm/views.py | 3 ++ hb_server/server/settings.py | 3 +- hb_server/server/urls.py | 1 + 14 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 hb_server/apps/develop/migrations/__init__.py create mode 100644 hb_server/apps/develop/models.py create mode 100644 hb_server/apps/develop/tests.py create mode 100644 hb_server/apps/develop/urls.py create mode 100644 hb_server/apps/pm/migrations/0020_auto_20211229_0912.py diff --git a/hb_server/apps/develop/apps.py b/hb_server/apps/develop/apps.py index afbb5ed..8df4e56 100644 --- a/hb_server/apps/develop/apps.py +++ b/hb_server/apps/develop/apps.py @@ -1,7 +1,5 @@ from django.apps import AppConfig + class DevelopConfig(AppConfig): name = 'apps.develop' - verbose_name = '开发调试接口' - - diff --git a/hb_server/apps/develop/migrations/__init__.py b/hb_server/apps/develop/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hb_server/apps/develop/models.py b/hb_server/apps/develop/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/hb_server/apps/develop/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/hb_server/apps/develop/tests.py b/hb_server/apps/develop/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/hb_server/apps/develop/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/hb_server/apps/develop/urls.py b/hb_server/apps/develop/urls.py new file mode 100644 index 0000000..6c1c780 --- /dev/null +++ b/hb_server/apps/develop/urls.py @@ -0,0 +1,10 @@ +from django.db.models import base +from rest_framework import urlpatterns +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from apps.develop.views import CleanDataView + +urlpatterns = [ + path('cleandata/', CleanDataView.as_view()), +] + diff --git a/hb_server/apps/develop/views.py b/hb_server/apps/develop/views.py index 69c1d41..ae3a2d8 100644 --- a/hb_server/apps/develop/views.py +++ b/hb_server/apps/develop/views.py @@ -1,12 +1,20 @@ from django.shortcuts import render -from rest_framework.exceptions import APIException from rest_framework.views import APIView -from rest_framework.viewsets import ModelViewSet - -from rest_framework import serializers, status +from rest_framework.permissions import IsAdminUser from rest_framework.response import Response +from apps.inm.models import FIFO +from apps.mtm.models import Material +from apps.sam.models import Order +# Create your views here. +class CleanDataView(APIView): + permission_classes = [IsAdminUser] - -class CleanData(APIView): - pass + def post(self, request, format=None): + """ + 清空数据库 + """ + Order.objects.delete() + FIFO.objects.delete() + Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() + return Response() \ No newline at end of file diff --git a/hb_server/apps/pm/migrations/0020_auto_20211229_0912.py b/hb_server/apps/pm/migrations/0020_auto_20211229_0912.py new file mode 100644 index 0000000..b914ef2 --- /dev/null +++ b/hb_server/apps/pm/migrations/0020_auto_20211229_0912.py @@ -0,0 +1,48 @@ +# Generated by Django 3.2.9 on 2021-12-29 01:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0019_alter_productionplan_process_json'), + ] + + operations = [ + migrations.AddField( + model_name='productionplan', + name='count_notok', + field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'), + ), + migrations.AddField( + model_name='subproductionplan', + name='main_count_notok', + field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'), + ), + migrations.AddField( + model_name='subproductionprogress', + name='count_notok', + field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count', + field=models.PositiveIntegerField(verbose_name='应出入数'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count_ok', + field=models.PositiveIntegerField(default=0, verbose_name='合格数量'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count_pick', + field=models.PositiveIntegerField(default=0, verbose_name='实际领用数'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count_real', + field=models.PositiveIntegerField(default=0, verbose_name='实际消耗/产出数'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index d9bf331..327209a 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -27,6 +27,7 @@ class ProductionPlan(CommonAModel): count = models.PositiveIntegerField('生产数量', default=1) count_real = models.PositiveIntegerField('实际产出数', default=0) count_ok = models.PositiveIntegerField('合格数', default=0) + count_notok = models.PositiveIntegerField('不合格数量', default=0) start_date = models.DateField('计划开工日期') end_date = models.DateField('计划完工日期') process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True) @@ -67,6 +68,7 @@ class SubProductionPlan(CommonAModel): main_count = models.PositiveIntegerField('应产出数', default=0) main_count_real = models.PositiveIntegerField('实际产出数', default=0) main_count_ok = models.PositiveIntegerField('合格数', default=0) + main_count_notok = models.PositiveIntegerField('不合格数量', default=0) steps = models.JSONField('工艺步骤', default=list) @@ -88,7 +90,8 @@ class SubProductionProgress(BaseModel): material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) is_main = models.BooleanField('是否主产出', default=False) type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices) - count = models.IntegerField('应出入数') - count_pick = models.IntegerField('实际领用数', default=0) - count_real = models.IntegerField('实际消耗/产出数', default=0) - count_ok = models.IntegerField('合格数量', default=0) + count = models.PositiveIntegerField('应出入数') + count_pick = models.PositiveIntegerField('实际领用数', default=0) + count_real = models.PositiveIntegerField('实际消耗/产出数', default=0) + count_ok = models.PositiveIntegerField('合格数量', default=0) + count_notok = models.PositiveIntegerField('不合格数量', default=0) diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py index afd8411..5d1d209 100644 --- a/hb_server/apps/pm/signals.py +++ b/hb_server/apps/pm/signals.py @@ -17,6 +17,7 @@ def update_subplan_main(sender, instance, created, **kwargs): subplan.main_count_real = instance.count_real subplan.main_count_ok = instance.count_ok + subplan.main_count_notok = instance.count_notok if instance.count_ok >= instance.count and instance.count_ok > 0: subplan.state = SubProductionPlan.SUBPLAN_STATE_DONE elif instance.count_ok < instance.count and instance.count_ok > 0: @@ -27,6 +28,7 @@ def update_subplan_main(sender, instance, created, **kwargs): plan = subplan.production_plan plan.count_real = subplan.main_count_real plan.count_ok = subplan.main_count_ok + plan.count_notok = subplan.main_count_notok plan.save() # 更新计划工序统计字段 PmService.update_plan_process_json(subplan.production_plan) diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 096b514..8974041 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -1,3 +1,4 @@ +from django.utils import timezone from typing import List from django.db.models.expressions import F @@ -67,6 +68,7 @@ class WpmServies(object): wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK # 需要走不合格品审理的工单 wproduct.update_by = user + wproduct.update_time = timezone.now() wproduct.test = None wproduct.save() @@ -78,10 +80,13 @@ class WpmServies(object): objs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True) count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM, WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).count() + count_notok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).count() count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST, - WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]) - ins = SubProductionProgress.objects.get(subproduction_plan=sp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - ins.count_ok = count_ok - ins.count_real = count_real - ins.save() \ No newline at end of file + WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count() + ins = SubProductionProgress.objects.filter(subproduction_plan=sp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).first() + if ins: + ins.count_ok = count_ok + ins.count_notok = count_notok + ins.count_real = count_real + ins.save() \ No newline at end of file diff --git a/hb_server/apps/wpm/signals.py b/hb_server/apps/wpm/signals.py index 4c936b8..57d398b 100644 --- a/hb_server/apps/wpm/signals.py +++ b/hb_server/apps/wpm/signals.py @@ -88,6 +88,7 @@ def update_wproduct_log(sender, instance, created, **kwargs): """ 更新产品变动日志 """ + # update_fields = kwargs['update_fields'] WproductFlow.objects.filter(wproduct=instance, subproduction_plan=instance.subproduction_plan).update(is_lastlog=False) ins = WproductFlow() ins.wproduct = instance diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index e11db5d..c20c99a 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -233,6 +233,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet): savedict['type'] = TestRecord.TEST_COMB tr = TestRecord.objects.create(**savedict) wproduct.test = tr + wproduct.update_by = request.user + wproduct.update_time = timezone.now() wproduct.save() # 创建检验条目 for i in RecordFormField.objects.filter(form=form, is_deleted=False): @@ -366,6 +368,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): raise exceptions.APIException('该产品不可报废') obj.act_state = WProduct.WPR_ACT_STATE_SCRAP obj.update_by = request.user + obj.update_time = timezone.now() obj.save() return Response() diff --git a/hb_server/server/settings.py b/hb_server/server/settings.py index 7338007..7c3dc19 100644 --- a/hb_server/server/settings.py +++ b/hb_server/server/settings.py @@ -58,7 +58,8 @@ INSTALLED_APPS = [ 'apps.qm', 'apps.pm', 'apps.wpm', - 'apps.srm' + 'apps.srm', + 'apps.develop' ] MIDDLEWARE = [ diff --git a/hb_server/server/urls.py b/hb_server/server/urls.py index b1893f2..65fdbf1 100644 --- a/hb_server/server/urls.py +++ b/hb_server/server/urls.py @@ -70,6 +70,7 @@ urlpatterns = [ path('api/pm/', include('apps.pm.urls')), path('api/wpm/', include('apps.wpm.urls')), path('api/srm/', include('apps.srm.urls')), + path('api/develop/', include('apps.develop.urls')), # 工具 path('api/utils/signature/', GenSignature.as_view()), path('api/utils/develop/', UpdateDevelop.as_view()), From d87ba485262b406288cdf959e56d268ecef364f3 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Dec 2021 10:07:08 +0800 Subject: [PATCH 33/36] =?UTF-8?q?=E8=BF=94=E5=B7=A5=E8=BF=94=E4=BF=AE=20?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=20retest=5Fresult=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/develop/views.py | 16 ++++++++++++---- hb_server/apps/wpm/services.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/hb_server/apps/develop/views.py b/hb_server/apps/develop/views.py index ae3a2d8..b3cf203 100644 --- a/hb_server/apps/develop/views.py +++ b/hb_server/apps/develop/views.py @@ -2,9 +2,12 @@ from django.shortcuts import render from rest_framework.views import APIView from rest_framework.permissions import IsAdminUser from rest_framework.response import Response -from apps.inm.models import FIFO +from apps.inm.models import FIFO, Inventory, MaterialBatch from apps.mtm.models import Material +from apps.pm.models import ProductionPlan from apps.sam.models import Order +from apps.wf.models import Ticket +from apps.wpm.models import Operation # Create your views here. class CleanDataView(APIView): @@ -14,7 +17,12 @@ class CleanDataView(APIView): """ 清空数据库 """ - Order.objects.delete() - FIFO.objects.delete() - Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() + Order.objects.all().delete(soft=False) + ProductionPlan.objects.all().delete(soft=False) + FIFO.objects.all().delete(soft=False) + Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).update(count=0) + MaterialBatch.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() + Inventory.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() + Ticket.objects.all().delete(soft=False) + Operation.objects.all().delete() return Response() \ No newline at end of file diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 8974041..4260fa0 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -6,6 +6,7 @@ from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.mtm.models import Material, Step, SubprodctionMaterial from apps.qm.models import TestRecord from apps.system.models import User +from apps.wf.models import State, TicketFlow, Transition from apps.wpm.models import WProduct, WproductFlow, WprouctTicket from utils.tools import ranstr class WpmServies(object): @@ -62,11 +63,39 @@ class WpmServies(object): wt = WprouctTicket.objects.order_by('id').last() #取最后的工单 if wt.step.process == test.step.process: wproduct.ng_sign = None + ticket = wt.ticket + ticket_data = wt.ticket_data + ticket_data['retest_result'] = 1 + ticket.update_by = user + ticket.save() + # 创建处理日志 + TicketFlow.objects.create(ticket=ticket, state=ticket.state, + participant_type=State.PARTICIPANT_TYPE_PERSONAL, + intervene_type=0, + participant=user) + + # 更新子计划相关进度 cls.update_subproduction_progress_main(sp=wproduct.subproduction_plan) else:# 如果不合格 wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK # 需要走不合格品审理的工单 + # 如果已经是返工返修的产品 + if wproduct.ng_sign: + wt = WprouctTicket.objects.order_by('id').last() #取最后的工单 + if wt.step.process == test.step.process: + ticket = wt.ticket + ticket_data = wt.ticket_data + ticket_data['retest_result'] = 0 + ticket.update_by = user + ticket.save() + # 创建处理日志 + TicketFlow.objects.create(ticket=ticket, state=ticket.state, + participant_type=State.PARTICIPANT_TYPE_PERSONAL, + intervene_type=0, + participant=user) + + wproduct.update_by = user wproduct.update_time = timezone.now() wproduct.test = None From 3c474ea8058e4f574f9c3db067527b8e6db1d5a7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Dec 2021 10:10:33 +0800 Subject: [PATCH 34/36] update_process_json --- hb_server/apps/pm/services.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/pm/services.py b/hb_server/apps/pm/services.py index 0b6894d..ade366f 100644 --- a/hb_server/apps/pm/services.py +++ b/hb_server/apps/pm/services.py @@ -18,7 +18,8 @@ class PmService: '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 + 'count_notok':i['count_notok'], + 'rate': round((i['count_ok']/(i['count_ok']+i['count_notok']))*100,2) if (i['count_ok']+i['count_notok']) > 0 else 0 } plan.process_json = ret plan.save() From 8a3de0accc8318a18a0ce3a79658a36a7b192f99 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Dec 2021 10:12:45 +0800 Subject: [PATCH 35/36] update_process_json --- hb_server/apps/pm/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/pm/services.py b/hb_server/apps/pm/services.py index ade366f..1bc6159 100644 --- a/hb_server/apps/pm/services.py +++ b/hb_server/apps/pm/services.py @@ -11,7 +11,7 @@ class PmService: 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')) + count_real=Sum('main_count_real'), count_ok=Sum('main_count_ok'), count_notok=Sum('main_count_notok')) qs_list = list(qs) for i in qs_list: ret[i['process__number']] = { From c596e79350bd24979a4ee80ce09940cb063f7e3e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 29 Dec 2021 10:16:54 +0800 Subject: [PATCH 36/36] genplan atomic --- hb_server/apps/pm/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 9f3d8fd..48a49e5 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -21,6 +21,7 @@ from rest_framework.response import Response from rest_framework.decorators import action from django.db.models import F from utils.tools import ranstr +from django.db import transaction # Create your views here. def updateOrderPlanedCount(order): @@ -77,6 +78,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel return Response() @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer) + @transaction.atomic def gen_subplan(self, request, pk=None): """ 生成子计划