From e07b5a4c5d709a20c6e7401c15c7dc852d90bf81 Mon Sep 17 00:00:00 2001 From: shijing Date: Mon, 17 Jan 2022 14:16:37 +0800 Subject: [PATCH 01/34] somewarnfixandtestshow --- .../components/Gantt/components/leftMenu.vue | 2 +- hb_client/src/views/dashboard/index.vue | 100 +++- hb_client/src/views/pm/plandetails.vue | 478 +++++++-------- hb_client/src/views/qm/producttest.vue | 1 - hb_client/src/views/qm/taskdetails.vue | 548 +++++++++--------- hb_client/src/views/qm/taskrecordfrom.vue | 1 - hb_client/src/views/wpm/need.vue | 84 ++- 7 files changed, 609 insertions(+), 605 deletions(-) diff --git a/hb_client/src/components/Gantt/components/leftMenu.vue b/hb_client/src/components/Gantt/components/leftMenu.vue index 632bcef..43a331f 100644 --- a/hb_client/src/components/Gantt/components/leftMenu.vue +++ b/hb_client/src/components/Gantt/components/leftMenu.vue @@ -19,7 +19,7 @@ @expand-change="handlerExpand" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > - + diff --git a/hb_client/src/views/dashboard/index.vue b/hb_client/src/views/dashboard/index.vue index 7ff725e..8536aa4 100644 --- a/hb_client/src/views/dashboard/index.vue +++ b/hb_client/src/views/dashboard/index.vue @@ -30,6 +30,17 @@
累计不合格产品数
+ +
甘特图
+ +
+ +
+
@@ -108,7 +119,7 @@ - + + <!–
库存列表
–>
提醒
- + <!–
提醒
–>
{{item}}2021-12-20
- + –> @@ -233,20 +244,23 @@ -
+ --> + diff --git a/hb_client/src/views/qm/producttest.vue b/hb_client/src/views/qm/producttest.vue index ef5b134..c8e9e87 100644 --- a/hb_client/src/views/qm/producttest.vue +++ b/hb_client/src/views/qm/producttest.vue @@ -119,7 +119,6 @@ :title="formName" :visible.sync="recordVisible" :close-on-click-modal="false" - @close="recordCancel" > diff --git a/hb_client/src/views/qm/taskdetails.vue b/hb_client/src/views/qm/taskdetails.vue index 37a1a93..117c79e 100644 --- a/hb_client/src/views/qm/taskdetails.vue +++ b/hb_client/src/views/qm/taskdetails.vue @@ -1,305 +1,283 @@ -
- - - - + + + + - - - - diff --git a/hb_client/src/views/qm/taskrecordfrom.vue b/hb_client/src/views/qm/taskrecordfrom.vue index c12eff3..f5edea3 100644 --- a/hb_client/src/views/qm/taskrecordfrom.vue +++ b/hb_client/src/views/qm/taskrecordfrom.vue @@ -46,7 +46,6 @@ :title="formName" :visible.sync="recordVisible" :close-on-click-modal="false" - @close="recordCancel" > diff --git a/hb_client/src/views/wpm/need.vue b/hb_client/src/views/wpm/need.vue index 5c58541..8c9e79c 100644 --- a/hb_client/src/views/wpm/need.vue +++ b/hb_client/src/views/wpm/need.vue @@ -613,6 +613,27 @@ 确 定 + + + + +
+ {{item.field_name}}: + {{item.field_value}} +
+
+ +
+ {{item.field_name}}: + +
+
+
+
+ From 0a83b8c2c2e2b12ec4863eb787f870ff2f595b92 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 17 Jan 2022 14:39:37 +0800 Subject: [PATCH 02/34] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=BA=A4=E4=BB=98?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E6=95=B0=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/filters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py index db876ec..bc97441 100644 --- a/hb_server/apps/inm/filters.py +++ b/hb_server/apps/inm/filters.py @@ -21,7 +21,8 @@ class MbFilterSet(filters.FilterSet): class IProductFilterSet(filters.FilterSet): order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order") - + update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte') + update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte') class Meta: model = IProduct - fields = ['material', 'warehouse', 'batch', 'order', 'material__type'] + fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'is_saled', 'update_time_start', 'update_time_end'] From b8824e94ac96308ece108a366f23ff5fd64806af Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 18 Jan 2022 08:42:26 +0800 Subject: [PATCH 03/34] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=97=B6=E5=90=8C=E6=AD=A5=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0010_alter_equipment_state.py | 18 +++++++++++++++++ hb_server/apps/em/models.py | 15 +++++++++----- hb_server/apps/hrm/models.py | 8 ++++---- .../0023_alter_productionplan_order.py | 20 +++++++++++++++++++ .../migrations/0049_operationequip_state.py | 18 +++++++++++++++++ hb_server/apps/wpm/models.py | 1 + hb_server/apps/wpm/views.py | 1 + 7 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 hb_server/apps/em/migrations/0010_alter_equipment_state.py create mode 100644 hb_server/apps/pm/migrations/0023_alter_productionplan_order.py create mode 100644 hb_server/apps/wpm/migrations/0049_operationequip_state.py diff --git a/hb_server/apps/em/migrations/0010_alter_equipment_state.py b/hb_server/apps/em/migrations/0010_alter_equipment_state.py new file mode 100644 index 0000000..4f8de3c --- /dev/null +++ b/hb_server/apps/em/migrations/0010_alter_equipment_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-18 00:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0009_auto_20210916_1108'), + ] + + operations = [ + migrations.AlterField( + model_name='equipment', + name='state', + field=models.PositiveIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='设备状态'), + ), + ] diff --git a/hb_server/apps/em/models.py b/hb_server/apps/em/models.py index f2cb67c..6838b99 100644 --- a/hb_server/apps/em/models.py +++ b/hb_server/apps/em/models.py @@ -14,11 +14,16 @@ class Equipment(CommonBModel): """ 设备台账信息 """ + EQUIP_STATE_OK = 0 + EQUIP_STATE_LIMIT = 1 + EQUIP_STATE_FIX = 2 + EQUIP_STATE_DISABLE = 3 state_choices = ( - (0, '完好'), - (1, '限用'), - (2, '在修'), - (3, '禁用') + (EQUIP_STATE_OK, '完好'), + (EQUIP_STATE_LIMIT, '限用'), + (EQUIP_STATE_FIX, '在修'), + (EQUIP_STATE_DISABLE, '禁用') + ) statedm_choices = ( (0, '合格'), @@ -54,7 +59,7 @@ class Equipment(CommonBModel): factory = models.CharField('生产厂', max_length=50, null=True, blank=True) production_date = models.DateField('生产日期', null=True, blank=True) buy_date = models.DateField('购置日期', null=True, blank=True) - state = models.CharField('设备状态', max_length=11, choices=state_choices, default=0) + state = models.PositiveIntegerField('设备状态', choices=state_choices, default=0) parameter = models.TextField('技术参数', null=True, blank=True) place = models.CharField('存放位置', max_length=50, null=True, blank=True) count = models.IntegerField('数量', default=0) diff --git a/hb_server/apps/hrm/models.py b/hb_server/apps/hrm/models.py index dc85cf5..5f95387 100644 --- a/hb_server/apps/hrm/models.py +++ b/hb_server/apps/hrm/models.py @@ -36,8 +36,8 @@ class Employee(CommonAModel): def __str__(self): return self.name -class Attendance(CommonADModel): - """ - 出勤记录 - """ +# class Attendance(CommonADModel): +# """ +# 出勤记录 +# """ \ No newline at end of file diff --git a/hb_server/apps/pm/migrations/0023_alter_productionplan_order.py b/hb_server/apps/pm/migrations/0023_alter_productionplan_order.py new file mode 100644 index 0000000..4fd00c6 --- /dev/null +++ b/hb_server/apps/pm/migrations/0023_alter_productionplan_order.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.9 on 2022-01-18 00:39 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sam', '0010_auto_20211208_1408'), + ('pm', '0022_auto_20211229_1429'), + ] + + operations = [ + migrations.AlterField( + model_name='productionplan', + name='order', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_order', to='sam.order', verbose_name='关联订单'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0049_operationequip_state.py b/hb_server/apps/wpm/migrations/0049_operationequip_state.py new file mode 100644 index 0000000..5541350 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0049_operationequip_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-18 00:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0048_operationwproduct_place'), + ] + + operations = [ + migrations.AddField( + model_name='operationequip', + name='state', + field=models.PositiveSmallIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='当前设备状态'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index dd870b4..b259eac 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -336,4 +336,5 @@ class OperationEquip(BaseModel): Operation, verbose_name='关联操作', on_delete=models.CASCADE, related_name='oe_operation') equip = models.ForeignKey(Equipment, verbose_name='生产设备', on_delete=models.CASCADE, related_name='oe_equip') + state = models.PositiveSmallIntegerField('当前设备状态', choices=Equipment.state_choices, default=Equipment.EQUIP_STATE_OK) remark = models.TextField('备注', null=True, blank=True) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 28d8a12..475b0d1 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -559,6 +559,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd ope = OperationEquip() ope.operation = op ope.equip = i + ope.state = i.state ope.save() # 查询所需的工具工装 for i in SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL, From 8dcabaf8dc72dc413ed9b40ab76af075dcd0d737 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 19 Jan 2022 14:37:13 +0800 Subject: [PATCH 04/34] =?UTF-8?q?wproduct=20notok=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + hb_server/apps/wpm/filters.py | 3 +++ hb_server/apps/wpm/views.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fa116e5..2f02e50 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ deploy.sh package-lock.json .idea/ .vscode/ +.idea/ server/static/ \ No newline at end of file diff --git a/hb_server/apps/wpm/filters.py b/hb_server/apps/wpm/filters.py index c8392d3..d85c1cd 100644 --- a/hb_server/apps/wpm/filters.py +++ b/hb_server/apps/wpm/filters.py @@ -41,6 +41,9 @@ class WProductFilterSet(filters.FilterSet): def filter_tag(self, queryset, name, value): if value == 'no_scrap': queryset = queryset.exclude(act_state=WProduct.WPR_ACT_STATE_SCRAP) + elif value == 'notok': + queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP])\ + .exclude(step__process__id = 1) # 不算冷加工的报废 return queryset diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 475b0d1..caababf 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -16,7 +16,7 @@ from rest_framework.decorators import action from apps.wf.models import Workflow from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, \ - OperationMaterial, OperationRecord, OperationRecordItem, WprouctTicket + OperationMaterial, OperationRecord, OperationRecordItem, WproductFlow, WprouctTicket from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerializer, OperationEquipUpdateSerializer, \ OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \ From aa10f7cee12918fb74d95274206f601c7315277d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 19 Jan 2022 14:41:00 +0800 Subject: [PATCH 05/34] wpm filter notok --- hb_server/apps/wpm/filters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hb_server/apps/wpm/filters.py b/hb_server/apps/wpm/filters.py index d85c1cd..54454f8 100644 --- a/hb_server/apps/wpm/filters.py +++ b/hb_server/apps/wpm/filters.py @@ -44,6 +44,9 @@ class WProductFilterSet(filters.FilterSet): elif value == 'notok': queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP])\ .exclude(step__process__id = 1) # 不算冷加工的报废 + elif value == 'ok': + queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM, + WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]) return queryset From f29a5ee5e4f7873ebf296372985833fd8adb0b47 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 19 Jan 2022 15:04:03 +0800 Subject: [PATCH 06/34] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E5=90=88=E6=A0=BC?= =?UTF-8?q?=E7=8E=87=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/srm/serializers.py | 6 +++- hb_server/apps/srm/urls.py | 3 +- hb_server/apps/srm/views.py | 47 +++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/hb_server/apps/srm/serializers.py b/hb_server/apps/srm/serializers.py index edea221..10e2b94 100644 --- a/hb_server/apps/srm/serializers.py +++ b/hb_server/apps/srm/serializers.py @@ -19,4 +19,8 @@ class PlanGanttSerializer(serializers.ModelSerializer): def get_children(self, obj): subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number') - return SubplanGanttSerializer(instance=subplans, many=True).data \ No newline at end of file + return SubplanGanttSerializer(instance=subplans, many=True).data + +class ProcessYieldSerializer(serializers.Serializer): + datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True) + datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True) \ No newline at end of file diff --git a/hb_server/apps/srm/urls.py b/hb_server/apps/srm/urls.py index cbaf2ed..0d8a992 100644 --- a/hb_server/apps/srm/urls.py +++ b/hb_server/apps/srm/urls.py @@ -3,11 +3,12 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.srm.views import GanttPlan +from apps.srm.views import GanttPlan, ProcessYieldView router = DefaultRouter() urlpatterns = [ path('gantt/plan/', GanttPlan.as_view()), + path('process/yield/', ProcessYieldView.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index 4551f30..175c847 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -1,9 +1,14 @@ + from django.shortcuts import render from rest_framework import serializers -from rest_framework.generics import ListAPIView +from rest_framework.generics import ListAPIView, CreateAPIView +from rest_framework.views import APIView from rest_framework.response import Response +from apps.mtm.models import Process, Step from apps.pm.models import ProductionPlan, SubProductionPlan -from apps.srm.serializers import PlanGanttSerializer +from apps.srm.serializers import PlanGanttSerializer, ProcessYieldSerializer +from apps.wpm.models import WProduct, WproductFlow +from django.db.models import Count # Create your views here. class GanttPlan(ListAPIView): @@ -15,4 +20,42 @@ class GanttPlan(ListAPIView): queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process') ordering = ['-id'] +class ProcessYieldView(CreateAPIView): + """ + 工序成品率统计 + """ + perms_map = {'get':'*'} + serializer_class = ProcessYieldSerializer + + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + wpfs = WproductFlow.objects.filter(is_lastlog=True) + if vdata.get('datetime_start', None): + wpfs = wpfs.filter(update_time__gte = vdata.get('datetime_start')) + if vdata.get('datetime_end', None): + wpfs = wpfs.filter(update_time__lte = vdata.get('datetime_end')) + # 根据产品日志记录进行聚合 + count_ok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM, + WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).values('step__process__id').annotate(count_ok=Count('id'))) + count_notok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).values('step__process__id', + ).annotate(count_notok=Count('id'))) + ret = [] + process_l = list(Process.objects.filter(is_deleted=False).order_by('number').values('id', 'name')) + for i in process_l: + ret_item = {'id':i['id'], 'name':i['name'], 'count_ok':0, 'count_notok':0, 'rate':0} + for m in count_ok_g: + if m['step__process__id'] == ret_item['id']: + ret_item['count_ok'] = m['count_ok'] + for n in count_notok_g: + if n['step__process__id'] == ret_item['id']: + ret_item['count_notok'] = n['count_notok'] + rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \ + if ret_item['count_ok']+ret_item['count_notok']>0 else 0 + ret_item['rate'] = rate + ret.append(ret_item) + return Response(ret) + From 5c2751baa9a01af9e3527efff0d9d4c4efef4591 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 19 Jan 2022 15:06:51 +0800 Subject: [PATCH 07/34] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E5=90=88=E6=A0=BC?= =?UTF-8?q?=E7=8E=87=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/srm/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index 175c847..57a6a75 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -45,7 +45,7 @@ class ProcessYieldView(CreateAPIView): ret = [] process_l = list(Process.objects.filter(is_deleted=False).order_by('number').values('id', 'name')) for i in process_l: - ret_item = {'id':i['id'], 'name':i['name'], 'count_ok':0, 'count_notok':0, 'rate':0} + ret_item = {'id':i['id'], 'name':i['name'], 'count_ok':0, 'count_notok':0, 'rate':1} for m in count_ok_g: if m['step__process__id'] == ret_item['id']: ret_item['count_ok'] = m['count_ok'] @@ -53,7 +53,7 @@ class ProcessYieldView(CreateAPIView): if n['step__process__id'] == ret_item['id']: ret_item['count_notok'] = n['count_notok'] rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \ - if ret_item['count_ok']+ret_item['count_notok']>0 else 0 + if ret_item['count_ok']+ret_item['count_notok']>0 else 1 ret_item['rate'] = rate ret.append(ret_item) return Response(ret) From 65f0d9f1ee7296f7e0fc281477526738f177fc13 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 19 Jan 2022 15:17:19 +0800 Subject: [PATCH 08/34] =?UTF-8?q?=E4=BA=A7=E5=93=81=E5=88=86=E8=A7=A3?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0need=5Fcombtest=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0044_subproduction_need_combtest.py | 18 ++++++++++++++++++ hb_server/apps/mtm/models.py | 1 + hb_server/apps/mtm/serializers.py | 5 +++++ hb_server/apps/mtm/views.py | 6 +++++- 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 hb_server/apps/mtm/migrations/0044_subproduction_need_combtest.py diff --git a/hb_server/apps/mtm/migrations/0044_subproduction_need_combtest.py b/hb_server/apps/mtm/migrations/0044_subproduction_need_combtest.py new file mode 100644 index 0000000..c570518 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0044_subproduction_need_combtest.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-19 07:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0043_auto_20220106_0942'), + ] + + operations = [ + migrations.AddField( + model_name='subproduction', + name='need_combtest', + field=models.BooleanField(default=False, verbose_name='需要质检'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index b7b99c2..4afb8b5 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -210,6 +210,7 @@ class SubProduction(CommonAModel): name = models.CharField('命名', max_length=50, null=True, blank=True) product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process') + need_combtest = models.BooleanField('需要质检', default=False) sort = models.IntegerField('排序号', default=1) class Meta: diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 8e5eb53..99cdfb6 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -72,6 +72,11 @@ class SubProductionSerializer(serializers.ModelSerializer): model = SubProduction fields = '__all__' +class SubProductionCreateUpdateSerializer(serializers.ModelSerializer): + class Meta: + model = SubProduction + fields = ['name', 'product', 'process', 'need_combtest', 'sort'] + class OtherMaterialSerializer(serializers.ModelSerializer): class Meta: model = SubprodctionMaterial diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 04f3caa..1bb9e47 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -4,7 +4,7 @@ from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelM from apps.mtm.filters import MaterialFilterSet, TechDocFilterset from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction -from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer +from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionCreateUpdateSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from rest_framework.response import Response @@ -80,6 +80,10 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet): search_fields = ['name'] serializer_class = SubProductionSerializer ordering = ['sort'] + def get_serializer_class(self): + if self.action in ['create', 'update']: + return SubProductionCreateUpdateSerializer + return super().get_serializer_class() class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): """ From db270f79149fc2444b1ee1b2217d804400b03807 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 08:42:40 +0800 Subject: [PATCH 09/34] =?UTF-8?q?update=5Fwproduct=5Fby=5Ftest=20=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E9=85=8D=E7=BD=AE=E6=98=AF=E5=90=A6=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E8=B4=A8=E6=A3=80=E5=86=B3=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/services.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 3734106..76a585d 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -54,7 +54,8 @@ class WpmServies(object): 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 - elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.step.type == Step.STEP_TYPE_COMB: # 夹层检验 + elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and \ + wproduct.subproduction_plan.subproduction.need_combtest : # 配置中需要质检 wproduct.act_state = WProduct.WPR_ACT_STATE_TOCOMBTEST else: wproduct.act_state = WProduct.WPR_ACT_STATE_OK From d01ca7df0f0ef326d1001427e935596746b3c9b9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 09:13:59 +0800 Subject: [PATCH 10/34] =?UTF-8?q?=E5=86=B7=E5=8A=A0=E5=B7=A5=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E5=90=88=E6=A0=BC=E7=8E=87=E4=B8=BA1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/srm/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index 57a6a75..b5a7158 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -50,7 +50,9 @@ class ProcessYieldView(CreateAPIView): if m['step__process__id'] == ret_item['id']: ret_item['count_ok'] = m['count_ok'] for n in count_notok_g: - if n['step__process__id'] == ret_item['id']: + if n['step__process__id'] == 1: # 如果是冷加工 + ret['count_notok'] = ret_item['count_ok'] + elif n['step__process__id'] == ret_item['id']: ret_item['count_notok'] = n['count_notok'] rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \ if ret_item['count_ok']+ret_item['count_notok']>0 else 1 From 8e8f231f72286d97c8ace09672e8e28542ec0f6a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 09:58:32 +0800 Subject: [PATCH 11/34] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../em/migrations/0011_auto_20220120_0956.py | 63 +++++++++++++++++++ hb_server/apps/em/models.py | 42 +++++-------- hb_server/apps/em/serializers.py | 4 +- hb_server/apps/em/urls.py | 4 +- hb_server/apps/em/views.py | 7 ++- 5 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 hb_server/apps/em/migrations/0011_auto_20220120_0956.py diff --git a/hb_server/apps/em/migrations/0011_auto_20220120_0956.py b/hb_server/apps/em/migrations/0011_auto_20220120_0956.py new file mode 100644 index 0000000..845ec26 --- /dev/null +++ b/hb_server/apps/em/migrations/0011_auto_20220120_0956.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.9 on 2022-01-20 01:56 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('em', '0010_alter_equipment_state'), + ] + + operations = [ + migrations.CreateModel( + name='ECheckRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('check_date', models.DateField(blank=True, null=True, verbose_name='校准检查日期')), + ('description', models.CharField(blank=True, max_length=200, null=True, verbose_name='描述')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='echeckrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='equipment', + name='belong_dept', + ), + migrations.RemoveField( + model_name='equipment', + name='statedm', + ), + migrations.AddField( + model_name='equipment', + name='next_check_date', + field=models.DateField(blank=True, null=True, verbose_name='下次校准检查日期'), + ), + migrations.AlterField( + model_name='equipment', + name='state', + field=models.PositiveIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=0, verbose_name='设备状态'), + ), + migrations.DeleteModel( + name='Equipmentrecord', + ), + migrations.AddField( + model_name='echeckrecord', + name='equipment', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'), + ), + migrations.AddField( + model_name='echeckrecord', + name='update_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='echeckrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'), + ), + ] diff --git a/hb_server/apps/em/models.py b/hb_server/apps/em/models.py index 6838b99..6b330e2 100644 --- a/hb_server/apps/em/models.py +++ b/hb_server/apps/em/models.py @@ -4,20 +4,19 @@ from django.db.models.base import Model import django.utils.timezone as timezone from django.db.models.query import QuerySet from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File -#from apps.mtm.models import Process from utils.model import SoftModel, BaseModel from simple_history.models import HistoricalRecords -class Equipment(CommonBModel): +class Equipment(CommonAModel): """ 设备台账信息 """ - EQUIP_STATE_OK = 0 - EQUIP_STATE_LIMIT = 1 - EQUIP_STATE_FIX = 2 - EQUIP_STATE_DISABLE = 3 + EQUIP_STATE_OK = 10 + EQUIP_STATE_LIMIT = 20 + EQUIP_STATE_FIX = 30 + EQUIP_STATE_DISABLE = 40 state_choices = ( (EQUIP_STATE_OK, '完好'), (EQUIP_STATE_LIMIT, '限用'), @@ -25,15 +24,6 @@ class Equipment(CommonBModel): (EQUIP_STATE_DISABLE, '禁用') ) - statedm_choices = ( - (0, '合格'), - (1, '准用'), - (2, '限用'), - (3, '禁用'), - (4, '停用'), - (5, '封存') - ) - type_choices = ( (1, '生产设备'), (2, '检验工具') @@ -65,14 +55,14 @@ class Equipment(CommonBModel): count = models.IntegerField('数量', default=0) keeper = models.ForeignKey(User, verbose_name='保管人', on_delete=models.CASCADE, null=True, blank=True) description = models.CharField('描述', max_length=200, blank=True, null=True) - #process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE, null=True, blank=True) - mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)#监视,测量设备 - way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)#监视,测量设备 - standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)#监视,测量设备 - cycle = models.IntegerField('校准或检定周期', default=0)#监视,测量设备 - usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)#监视,测量设备 - statedm = models.IntegerField('设备状态', choices=statedm_choices, default=0)#监视,测量设备 + # 以下是监视测量设备单独字段 + mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1) + way = models.IntegerField('校准或检定方式', choices=way_choices, default=1) + standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True) + cycle = models.IntegerField('校准或检定周期', default=0) + usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1) + next_check_date = models.DateField('下次校准检查日期',blank=True, null=True) class Meta: verbose_name = '设备信息' verbose_name_plural = verbose_name @@ -80,8 +70,10 @@ class Equipment(CommonBModel): def __str__(self): return self.number + '-' + self.name -class Equipmentrecord(CommonBModel): +class ECheckRecord(CommonAModel): + """ + 校准鉴定记录 + """ equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE, null=True, blank=True) - recentlydate = models.DateField('最近一次校准/检定日期',blank=True, null=True) - nextdate = models.DateField('下次应校准或检定日期',blank=True, null=True) + check_date = models.DateField('校准检查日期',blank=True, null=True) description = models.CharField('描述', max_length=200, blank=True, null=True) \ No newline at end of file diff --git a/hb_server/apps/em/serializers.py b/hb_server/apps/em/serializers.py index be34bfc..ce3908e 100644 --- a/hb_server/apps/em/serializers.py +++ b/hb_server/apps/em/serializers.py @@ -2,7 +2,7 @@ from apps.mtm.models import Step from rest_framework import serializers from rest_framework.serializers import ModelSerializer -from .models import Equipment,Equipmentrecord +from .models import Equipment, ECheckRecord from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer @@ -34,7 +34,7 @@ class EquipmentSimpleSerializer(ModelSerializer): class EquipmentrecordSerializer(ModelSerializer): equipment_ = EquipmentSerializer(source='equipment', read_only=True) class Meta: - model = Equipmentrecord + model = ECheckRecord fields = '__all__' @staticmethod diff --git a/hb_server/apps/em/urls.py b/hb_server/apps/em/urls.py index e2f7e91..2fc4bb8 100644 --- a/hb_server/apps/em/urls.py +++ b/hb_server/apps/em/urls.py @@ -1,12 +1,12 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.em.views import DaqView, EquipmentViewSet,EquipmentrecordViewSet +from apps.em.views import DaqView, EquipmentViewSet, EChcekRecordViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('equipment', EquipmentViewSet, basename='equipment') -router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord') +router.register('echeck_record', EChcekRecordViewSet, basename='echeck_record') urlpatterns = [ path('daq/', DaqView.as_view()), path('', include(router.urls)), diff --git a/hb_server/apps/em/views.py b/hb_server/apps/em/views.py index 00ae4b9..7e526bf 100644 --- a/hb_server/apps/em/views.py +++ b/hb_server/apps/em/views.py @@ -5,7 +5,7 @@ from rest_framework.viewsets import ModelViewSet from rest_framework import serializers, status from rest_framework.response import Response -from apps.em.models import Equipment,Equipmentrecord +from apps.em.models import Equipment, ECheckRecord from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin @@ -26,13 +26,13 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet) # Create your views here. -class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): +class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): """ 设备校准-增删改查 """ perms_map = {'get': '*', 'post': 'equipmentrecord_create', 'put': 'equipmentrecord_update', 'delete': 'equipmentrecord_delete'} - queryset = Equipmentrecord.objects.all() + queryset = ECheckRecord.objects.all() serializer_class = EquipmentrecordSerializer filterset_fields = ['equipment'] ordering_fields = ['create_time'] @@ -49,6 +49,7 @@ class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelVi serializer.is_valid(raise_exception=True) serializer.save() return Response(status=status.HTTP_200_OK) + def update(self, request, *args, **kwargs): data = request.data if data.get('equipment', None): From cf19c022fae5830f7ea032369de4a7d8ad9f1d18 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 10:47:31 +0800 Subject: [PATCH 12/34] =?UTF-8?q?em=20=E6=9B=B4=E6=96=B0=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=20=E6=9C=AA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/em/models.py | 20 +++++++--- hb_server/apps/em/serializers.py | 26 +++++++++--- hb_server/apps/em/services.py | 7 ++++ hb_server/apps/em/views.py | 68 +++++++++++++++++--------------- hb_server/apps/pm/views.py | 4 +- 5 files changed, 80 insertions(+), 45 deletions(-) create mode 100644 hb_server/apps/em/services.py diff --git a/hb_server/apps/em/models.py b/hb_server/apps/em/models.py index 6b330e2..194474f 100644 --- a/hb_server/apps/em/models.py +++ b/hb_server/apps/em/models.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import AbstractUser from django.db.models.base import Model import django.utils.timezone as timezone from django.db.models.query import QuerySet -from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File +from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File from utils.model import SoftModel, BaseModel from simple_history.models import HistoricalRecords @@ -24,6 +24,13 @@ class Equipment(CommonAModel): (EQUIP_STATE_DISABLE, '禁用') ) + state2_choices = ( + (EQUIP_STATE_OK, '合格'), + (EQUIP_STATE_DISABLE, '禁用') + + ) + EQUIP_TYPE_PRO = 1 + EQUIP_TYPE_TEST = 2 type_choices = ( (1, '生产设备'), (2, '检验工具') @@ -59,10 +66,11 @@ class Equipment(CommonAModel): mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1) way = models.IntegerField('校准或检定方式', choices=way_choices, default=1) standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True) - cycle = models.IntegerField('校准或检定周期', default=0) + cycle = models.IntegerField('校准或检定周期(月)', null=True, blank=True) usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1) - next_check_date = models.DateField('下次校准检查日期',blank=True, null=True) + check_date = models.DateField('最近校准检查日期', blank=True, null=True) + next_check_date = models.DateField('预计下次校准检查日期',blank=True, null=True) class Meta: verbose_name = '设备信息' verbose_name_plural = verbose_name @@ -70,10 +78,10 @@ class Equipment(CommonAModel): def __str__(self): return self.number + '-' + self.name -class ECheckRecord(CommonAModel): +class ECheckRecord(CommonADModel): """ 校准鉴定记录 """ - equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE, null=True, blank=True) - check_date = models.DateField('校准检查日期',blank=True, null=True) + equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE) + check_date = models.DateField('校准检查日期') description = models.CharField('描述', max_length=200, blank=True, null=True) \ No newline at end of file diff --git a/hb_server/apps/em/serializers.py b/hb_server/apps/em/serializers.py index ce3908e..89900d3 100644 --- a/hb_server/apps/em/serializers.py +++ b/hb_server/apps/em/serializers.py @@ -1,12 +1,12 @@ from apps.mtm.models import Step from rest_framework import serializers from rest_framework.serializers import ModelSerializer - +from rest_framework import exceptions from .models import Equipment, ECheckRecord from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer -class EquipmentSerializer(ModelSerializer): +class EquipmentListSerializer(ModelSerializer): belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True) keeper_ = UserSimpleSerializer(source='keeper', read_only=True) step_ = serializers.SerializerMethodField() @@ -24,15 +24,26 @@ class EquipmentSerializer(ModelSerializer): return Step.objects.filter(equipments=obj).values('id', 'name', 'number') +class EquipmentCreateUpdateSerializer(ModelSerializer): + class Meta: + model = Equipment + exclude = ['create_by', 'update_by', 'create_time', 'update_time'] + + def validate(self, attrs): + if attrs['type'] == Equipment.EQUIP_TYPE_TEST: + if attrs['state'] not in [Equipment.EQUIP_STATE_OK, Equipment.EQUIP_STATE_DISABLE]: + raise exceptions.APIException('设备状态错误') + return super().validate(attrs) + class EquipmentSimpleSerializer(ModelSerializer): class Meta: model = Equipment - fields = ['id', 'number', 'name', 'state'] + fields = ['id', 'number', 'name', 'state', 'model'] -class EquipmentrecordSerializer(ModelSerializer): - equipment_ = EquipmentSerializer(source='equipment', read_only=True) +class ECheckRecordListSerializer(ModelSerializer): + equipment_ = EquipmentSimpleSerializer(source='equipment', read_only=True) class Meta: model = ECheckRecord fields = '__all__' @@ -43,6 +54,11 @@ class EquipmentrecordSerializer(ModelSerializer): queryset = queryset.select_related('equipment') return queryset +class EChcekRecordCreateSerializer(ModelSerializer): + class Meta: + model = ECheckRecord + fields = ['equipment', 'check_date', 'description'] + class DaqCreateSerializer(serializers.Serializer): number = serializers.CharField() file = serializers.FileField() \ No newline at end of file diff --git a/hb_server/apps/em/services.py b/hb_server/apps/em/services.py new file mode 100644 index 0000000..0f05e39 --- /dev/null +++ b/hb_server/apps/em/services.py @@ -0,0 +1,7 @@ +from apps.em.models import Equipment +class EmService: + + @classmethod + def update_check_date(cls, equip:Equipment): + # 根据校准检定记录变更下次日期 + pass \ No newline at end of file diff --git a/hb_server/apps/em/views.py b/hb_server/apps/em/views.py index 7e526bf..0d19abe 100644 --- a/hb_server/apps/em/views.py +++ b/hb_server/apps/em/views.py @@ -1,14 +1,20 @@ +from datetime import timedelta from django.shortcuts import render +from numpy import delete from rest_framework.exceptions import APIException from rest_framework.views import APIView -from rest_framework.viewsets import ModelViewSet - +from rest_framework.viewsets import ModelViewSet, GenericViewSet +from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin from rest_framework import serializers, status from rest_framework.response import Response from apps.em.models import Equipment, ECheckRecord -from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer +from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \ + EquipmentCreateUpdateSerializer, EquipmentListSerializer +from apps.em.services import EmService from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin - +from dateutil.relativedelta import relativedelta +from django.utils import timezone +from django.db import transaction # Create your views here. class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): @@ -18,49 +24,47 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet) perms_map = {'get': '*', 'post': 'equipment_create', 'put': 'equipment_update', 'delete': 'equipment_delete'} queryset = Equipment.objects.all() - serializer_class = EquipmentSerializer + serializer_class = EquipmentListSerializer search_fields = ['number', 'name','description'] - filterset_fields = ['belong_dept', 'keeper', 'type'] + filterset_fields = ['keeper', 'type'] ordering_fields = ['create_time'] ordering = ['-create_time'] + def get_serializer_class(self): + if self.action in ['create', 'update']: + return EquipmentCreateUpdateSerializer + return super().get_serializer_class() # Create your views here. -class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): +class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, + CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet): """ 设备校准-增删改查 """ - perms_map = {'get': '*', 'post': 'equipmentrecord_create', - 'put': 'equipmentrecord_update', 'delete': 'equipmentrecord_delete'} + perms_map = {'get': '*', 'post': 'echeckrecord_create', 'delete': 'echeckrecord_delete'} queryset = ECheckRecord.objects.all() - serializer_class = EquipmentrecordSerializer + serializer_class = ECheckRecordListSerializer filterset_fields = ['equipment'] - ordering_fields = ['create_time'] - ordering = ['-create_time'] + ordering = ['-id'] + def get_serializer_class(self): + if self.action in ['create']: + return EChcekRecordCreateSerializer + return super().get_serializer_class() + + @transaction.atomic def create(self, request, *args, **kwargs): - - data = request.data - if data.get('equipment', None): - equipment = Equipment.objects.get(pk=data['equipment']) - equipment.statedm = data['state'] - equipment.save() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - serializer.save() - return Response(status=status.HTTP_200_OK) - - def update(self, request, *args, **kwargs): - data = request.data - if data.get('equipment', None): - equipment = Equipment.objects.get(pk=data['equipment']) - equipment.statedm = data['state'] - equipment.save() - id = self.get_object() - serializer = self.get_serializer(id, data=data) - serializer.is_valid(raise_exception=True) - serializer.save() - return Response(status=status.HTTP_200_OK) + instance = serializer.save(create_by=request.user) + EmService.update_check_date(equip=instance) + return Response() + + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + EmService.update_check_date(equip=instance) + instance.delete() + return super().destroy(request, *args, **kwargs) import uuid import os diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 3fe8799..3761ead 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -4,7 +4,7 @@ from django.db import transaction from rest_framework import serializers from rest_framework.views import APIView from apps.em.models import Equipment -from apps.em.serializers import EquipmentSerializer +from apps.em.serializers import EquipmentSimpleSerializer from apps.inm.models import MaterialBatch from apps.inm.serializers import MaterialBatchSerializer from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep @@ -248,5 +248,5 @@ class ResourceViewSet(GenericViewSet): subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False) steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions) equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct() - serializer = EquipmentSerializer(instance=equips, many=True) + serializer = EquipmentSimpleSerializer(instance=equips, many=True) return Response(serializer.data) From e4c7f4856cbf603ee6a78bc4297645c23739cc92 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 10:49:05 +0800 Subject: [PATCH 13/34] =?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 --- .../em/migrations/0012_auto_20220120_1048.py | 43 +++++++++++++++++++ .../0050_alter_operationequip_state.py | 18 ++++++++ 2 files changed, 61 insertions(+) create mode 100644 hb_server/apps/em/migrations/0012_auto_20220120_1048.py create mode 100644 hb_server/apps/wpm/migrations/0050_alter_operationequip_state.py diff --git a/hb_server/apps/em/migrations/0012_auto_20220120_1048.py b/hb_server/apps/em/migrations/0012_auto_20220120_1048.py new file mode 100644 index 0000000..a0aff45 --- /dev/null +++ b/hb_server/apps/em/migrations/0012_auto_20220120_1048.py @@ -0,0 +1,43 @@ +# Generated by Django 3.2.9 on 2022-01-20 02:48 + +import datetime +from django.db import migrations, models +import django.db.models.deletion +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0011_auto_20220120_0956'), + ] + + operations = [ + migrations.AddField( + model_name='equipment', + name='check_date', + field=models.DateField(blank=True, null=True, verbose_name='最近校准检查日期'), + ), + migrations.AlterField( + model_name='echeckrecord', + name='check_date', + field=models.DateField(default=datetime.datetime(2022, 1, 20, 2, 48, 20, 706844, tzinfo=utc), verbose_name='校准检查日期'), + preserve_default=False, + ), + migrations.AlterField( + model_name='echeckrecord', + name='equipment', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'), + preserve_default=False, + ), + migrations.AlterField( + model_name='equipment', + name='cycle', + field=models.IntegerField(blank=True, null=True, verbose_name='校准或检定周期(月)'), + ), + migrations.AlterField( + model_name='equipment', + name='next_check_date', + field=models.DateField(blank=True, null=True, verbose_name='预计下次校准检查日期'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0050_alter_operationequip_state.py b/hb_server/apps/wpm/migrations/0050_alter_operationequip_state.py new file mode 100644 index 0000000..a04569a --- /dev/null +++ b/hb_server/apps/wpm/migrations/0050_alter_operationequip_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-20 02:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0049_operationequip_state'), + ] + + operations = [ + migrations.AlterField( + model_name='operationequip', + name='state', + field=models.PositiveSmallIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=10, verbose_name='当前设备状态'), + ), + ] From accfedb1d75ee8e645fbd5ded38d659ad1514657 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 10:57:32 +0800 Subject: [PATCH 14/34] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=A0=A1=E5=87=86?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=8F=98=E6=9B=B4=E4=B8=8B=E6=AC=A1=E6=A0=A1?= =?UTF-8?q?=E5=87=86=E6=97=A5=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/em/serializers.py | 2 +- hb_server/apps/em/services.py | 15 +++++++++++++-- hb_server/apps/em/views.py | 9 +++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hb_server/apps/em/serializers.py b/hb_server/apps/em/serializers.py index 89900d3..e60b2a4 100644 --- a/hb_server/apps/em/serializers.py +++ b/hb_server/apps/em/serializers.py @@ -27,7 +27,7 @@ class EquipmentListSerializer(ModelSerializer): class EquipmentCreateUpdateSerializer(ModelSerializer): class Meta: model = Equipment - exclude = ['create_by', 'update_by', 'create_time', 'update_time'] + exclude = ['create_by', 'update_by', 'create_time', 'update_time', 'check_date', 'next_check_date'] def validate(self, attrs): if attrs['type'] == Equipment.EQUIP_TYPE_TEST: diff --git a/hb_server/apps/em/services.py b/hb_server/apps/em/services.py index 0f05e39..15f5b08 100644 --- a/hb_server/apps/em/services.py +++ b/hb_server/apps/em/services.py @@ -1,7 +1,18 @@ -from apps.em.models import Equipment +from tabnanny import check +from apps.em.models import ECheckRecord, Equipment +from dateutil.relativedelta import relativedelta +from django.utils import timezone class EmService: @classmethod def update_check_date(cls, equip:Equipment): # 根据校准检定记录变更下次日期 - pass \ No newline at end of file + check = ECheckRecord.objects.filter(equipment=equip).order_by('id').last() + if check: + if equip.cycle: + equip.check_date = check.check_date + if equip.next_check_date: + equip.next_check_date = equip.next_check_date + relativedelta(months=equip.cycle) + else: + equip.next_check_date = timezone.now() + relativedelta(months=equip.cycle) + equip.save() \ No newline at end of file diff --git a/hb_server/apps/em/views.py b/hb_server/apps/em/views.py index 0d19abe..aec05f7 100644 --- a/hb_server/apps/em/views.py +++ b/hb_server/apps/em/views.py @@ -12,7 +12,7 @@ from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerialize EquipmentCreateUpdateSerializer, EquipmentListSerializer from apps.em.services import EmService from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin -from dateutil.relativedelta import relativedelta + from django.utils import timezone from django.db import transaction @@ -57,14 +57,15 @@ class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) instance = serializer.save(create_by=request.user) - EmService.update_check_date(equip=instance) + EmService.update_check_date(equip=instance.equipment) return Response() + @transaction.atomic def destroy(self, request, *args, **kwargs): instance = self.get_object() - EmService.update_check_date(equip=instance) instance.delete() - return super().destroy(request, *args, **kwargs) + EmService.update_check_date(equip=instance.equipment) + return Response() import uuid import os From ddc54a8c762d081fa04c71ad58e0a21c065a0b8f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 11:01:18 +0800 Subject: [PATCH 15/34] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E8=AF=B7=E6=B1=82bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/em/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hb_server/apps/em/serializers.py b/hb_server/apps/em/serializers.py index e60b2a4..c22238d 100644 --- a/hb_server/apps/em/serializers.py +++ b/hb_server/apps/em/serializers.py @@ -7,7 +7,6 @@ from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSeri class EquipmentListSerializer(ModelSerializer): - belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True) keeper_ = UserSimpleSerializer(source='keeper', read_only=True) step_ = serializers.SerializerMethodField() class Meta: @@ -17,7 +16,7 @@ class EquipmentListSerializer(ModelSerializer): @staticmethod def setup_eager_loading(queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.select_related('belong_dept','keeper') + queryset = queryset.select_related('keeper') return queryset def get_step_(self, obj): From b9a9aa2091d1360aba286d38d02d8323f622e411 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 13:21:59 +0800 Subject: [PATCH 16/34] update check date bug --- hb_server/apps/em/services.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/em/services.py b/hb_server/apps/em/services.py index 15f5b08..0c1eba4 100644 --- a/hb_server/apps/em/services.py +++ b/hb_server/apps/em/services.py @@ -8,11 +8,13 @@ class EmService: def update_check_date(cls, equip:Equipment): # 根据校准检定记录变更下次日期 check = ECheckRecord.objects.filter(equipment=equip).order_by('id').last() + equip.check_date = check.check_date if check: if equip.cycle: - equip.check_date = check.check_date - if equip.next_check_date: - equip.next_check_date = equip.next_check_date + relativedelta(months=equip.cycle) - else: - equip.next_check_date = timezone.now() + relativedelta(months=equip.cycle) - equip.save() \ No newline at end of file + equip.next_check_date = equip.check_date + relativedelta(months=equip.cycle) + else: + equip.next_check_date = None + else: + equip.check_date = None + equip.next_check_date = None + equip.save() \ No newline at end of file From 0c5601f53afc0fa658a113c3eda55e9a66cb9a64 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 13:23:04 +0800 Subject: [PATCH 17/34] update check date bug --- hb_server/apps/em/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/em/services.py b/hb_server/apps/em/services.py index 0c1eba4..5e1781c 100644 --- a/hb_server/apps/em/services.py +++ b/hb_server/apps/em/services.py @@ -8,8 +8,8 @@ class EmService: def update_check_date(cls, equip:Equipment): # 根据校准检定记录变更下次日期 check = ECheckRecord.objects.filter(equipment=equip).order_by('id').last() - equip.check_date = check.check_date if check: + equip.check_date = check.check_date if equip.cycle: equip.next_check_date = equip.check_date + relativedelta(months=equip.cycle) else: From 7f4da0221ca8d4b10bae85e8cdaee09bbc6ba0fa Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 13:50:30 +0800 Subject: [PATCH 18/34] =?UTF-8?q?=E5=86=B7=E5=8A=A0=E5=B7=A5=E7=9A=84?= =?UTF-8?q?=E5=90=88=E6=A0=BC=E7=8E=87=E5=8D=95=E7=8B=AC=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/develop/urls.py | 5 +++-- hb_server/apps/develop/views.py | 13 ++++++++++++- hb_server/apps/srm/views.py | 12 ++++++++---- hb_server/apps/wpm/services.py | 6 +++++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/hb_server/apps/develop/urls.py b/hb_server/apps/develop/urls.py index b284243..79f8f04 100644 --- a/hb_server/apps/develop/urls.py +++ b/hb_server/apps/develop/urls.py @@ -2,13 +2,14 @@ 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, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult +from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg urlpatterns = [ path('cleandata/', CleanDataView.as_view()), path('update_cutting/', UpdateCuttingView.as_view()), path('update_last_result/', UpdateLastTestResult.as_view()), path('update_last_result/', UpdateLastTestResult.as_view()), - path('update_fifoitem/', UpdateFIFOItem.as_view()) + path('update_fifoitem/', UpdateFIFOItem.as_view()), + path('update_spg/', UpdateSpg.as_view()) ] diff --git a/hb_server/apps/develop/views.py b/hb_server/apps/develop/views.py index f02f55f..f6a52fc 100644 --- a/hb_server/apps/develop/views.py +++ b/hb_server/apps/develop/views.py @@ -6,7 +6,7 @@ from rest_framework.permissions import IsAdminUser from rest_framework.response import Response from apps.inm.models import FIFO, FIFOItem, Inventory, MaterialBatch from apps.mtm.models import Material -from apps.pm.models import ProductionPlan +from apps.pm.models import ProductionPlan, SubProductionPlan from apps.sam.models import Order from apps.wf.models import Ticket from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow @@ -80,3 +80,14 @@ class UpdateFIFOItem(APIView): i.is_testok = None i.save() return Response() + +class UpdateSpg(APIView): + permission_classes = [IsAdminUser] + @transaction.atomic + def post(self, request, format=None): + """ + 冷加工重新计算合格率 + """ + for i in SubProductionPlan.objects.filter(subproduction__process__id=1): + WpmServies.update_subproduction_progress_main(sp=i) + return Response() diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index b5a7158..ec7a398 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -40,7 +40,13 @@ class ProcessYieldView(CreateAPIView): # 根据产品日志记录进行聚合 count_ok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM, WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).values('step__process__id').annotate(count_ok=Count('id'))) - count_notok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).values('step__process__id', + count_notok_g = list( + ( + wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1) + | wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP], + step__process__id=1).exclude(number=None) + )\ + .values('step__process__id', ).annotate(count_notok=Count('id'))) ret = [] process_l = list(Process.objects.filter(is_deleted=False).order_by('number').values('id', 'name')) @@ -50,9 +56,7 @@ class ProcessYieldView(CreateAPIView): if m['step__process__id'] == ret_item['id']: ret_item['count_ok'] = m['count_ok'] for n in count_notok_g: - if n['step__process__id'] == 1: # 如果是冷加工 - ret['count_notok'] = ret_item['count_ok'] - elif n['step__process__id'] == ret_item['id']: + if n['step__process__id'] == ret_item['id']: ret_item['count_notok'] = n['count_notok'] rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \ if ret_item['count_ok']+ret_item['count_notok']>0 else 1 diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 76a585d..d880f7f 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -114,7 +114,11 @@ 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_notok = ( + objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1) + | objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP], + step__process__id=1).exclude(number=None) + ).count() count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count() ins = SubProductionProgress.objects.filter(subproduction_plan=sp, From b8346d90b94ed52111608162f7e541ce363487cf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 15:41:47 +0800 Subject: [PATCH 19/34] =?UTF-8?q?wproduct=20=E6=8C=87=E6=B4=BE=E8=AE=A2?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wpm/migrations/0051_auto_20220120_1541.py | 25 +++++++++++++++++ hb_server/apps/wpm/models.py | 5 ++-- hb_server/apps/wpm/serializers.py | 7 ++++- hb_server/apps/wpm/views.py | 28 +++++++++++++++++-- hb_server/utils/response.py | 25 +++++++++-------- 5 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 hb_server/apps/wpm/migrations/0051_auto_20220120_1541.py diff --git a/hb_server/apps/wpm/migrations/0051_auto_20220120_1541.py b/hb_server/apps/wpm/migrations/0051_auto_20220120_1541.py new file mode 100644 index 0000000..9a0c6fb --- /dev/null +++ b/hb_server/apps/wpm/migrations/0051_auto_20220120_1541.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.9 on 2022-01-20 07:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sam', '0010_auto_20211208_1408'), + ('wpm', '0050_alter_operationequip_state'), + ] + + operations = [ + migrations.AddField( + model_name='wproduct', + name='to_order', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'), + ), + migrations.AddField( + model_name='wproductflow', + name='to_order', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index b259eac..c52a061 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -118,7 +118,8 @@ class WProduct(CommonAModel): on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_test') ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket') - + + to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) is_mtested = models.BooleanField('是否军检', default=False) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True) @@ -192,7 +193,7 @@ class WproductFlow(CommonAModel): on_delete=models.SET_NULL, null=True, blank=True) ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', on_delete=models.SET_NULL, null=True, blank=True) - + to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE) is_mtested = models.BooleanField('是否军检', default=False) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 8956eb3..3e245ef 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -19,6 +19,7 @@ from apps.system.models import User from apps.system.serializers import UserSimpleSerializer from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket from django.db import transaction +from apps.sam.models import Order class PickHalfSerializer(serializers.Serializer): id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID') @@ -560,4 +561,8 @@ class CuttingListSerializer(serializers.ModelSerializer): class WproductMtestSerializer(serializers.ModelSerializer): class Meta: model = WProduct - fields = ['remark_mtest', 'is_mtestok'] \ No newline at end of file + fields = ['remark_mtest', 'is_mtestok'] + +class WproductToOrderSerializer(serializers.Serializer): + wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True) + order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all()) \ No newline at end of file diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index caababf..5847c89 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -27,7 +27,7 @@ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerial PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \ WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \ WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \ - WproductPutInsSerializer, WproductTicketListSerializer + WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer from rest_framework.response import Response from django.db import transaction @@ -441,7 +441,31 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): ret.append([str(index + 1), item['step_name'], item['actions']]) return Response(ret) - + @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductToOrderSerializer) + @transaction.atomic + def to_order(self, request, pk=None): + """ + 指派发货订单 + """ + serializer = WproductToOrderSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')]) + wp = wps.first() + order = vdata['order'] + if wp.material != order.product: + raise exceptions.ValidationError('所选订单与产品不符') + for i in wps: + if i.material != wp.material and i.material.type != Material.MA_TYPE_GOOD and i.act_state \ + not in [WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_INM]: + raise exceptions.ValidationError('所选产品错误') + for i in wps: + i.to_order = order + i.update_by = request.user + i.save() + WpmServies.add_wproduct_flow_log(i,change_str='to_order') + return Response() + class WproductTicketViewSet(ListModelMixin, GenericViewSet): """ 玻璃审批工单 diff --git a/hb_server/utils/response.py b/hb_server/utils/response.py index f4379f4..0411547 100644 --- a/hb_server/utils/response.py +++ b/hb_server/utils/response.py @@ -47,17 +47,20 @@ class FitJSONRenderer(JSONRenderer): """ response_body = BaseResponse() response = renderer_context.get("response") - response_body.code = response.status_code - if response_body.code >= 400: # 响应异常 - response_body.data = data # data里是详细异常信息 - prefix = "" - if isinstance(data, dict): - prefix = list(data.keys())[0] - data = data[prefix] - if isinstance(data, list): - data = data[0] - response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert + status_code = response.status_code # Http状态异常码 + if status_code >= 400: # 如果http响应异常 + if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码 + response_body = data + else: + response_body.data = data # data里是详细异常信息 + prefix = "" + if isinstance(data, dict): + prefix = list(data.keys())[0] + data = data[prefix] + elif isinstance(data, list): + data = data[0] + response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert else: response_body.data = data - renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分 + renderer_context.get("response").status_code = 200 # 统一成200响应, 可用body里code区分业务异常 return super(FitJSONRenderer, self).render(response_body.dict, accepted_media_type, renderer_context) From 28882ae074339ad55059c32294080a204e5848b8 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 15:44:27 +0800 Subject: [PATCH 20/34] =?UTF-8?q?wproductserializer=20=E5=A2=9E=E5=8A=A0to?= =?UTF-8?q?=5Forder=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/serializers.py | 1 + hb_server/apps/wpm/views.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 3e245ef..3181088 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -156,6 +156,7 @@ class WProductListSerializer(serializers.ModelSerializer): subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) children = serializers.SerializerMethodField() + to_order_ = OrderSimpleSerializer(source='to_order', read_only=True) class Meta: model = WProduct fields = '__all__' diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 5847c89..47c49dc 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -148,7 +148,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): """ perms_map = {'*': '*'} queryset = WProduct.objects.select_related('step', 'material', - 'subproduction_plan', 'warehouse').prefetch_related('wproduct_child') + 'subproduction_plan', 'warehouse', 'order').prefetch_related('wproduct_child') serializer_class = WProductListSerializer filterset_class = WProductFilterSet search_fields = ['number'] From 0796f30cb1da5926f89e615d1ffaa605dabe3021 Mon Sep 17 00:00:00 2001 From: shijing Date: Thu, 20 Jan 2022 15:45:32 +0800 Subject: [PATCH 21/34] dashPage --- hb_client/src/api/srm.js | 8 + .../Gantt/components/dashLeftMenu.vue | 121 ++ hb_client/src/components/Gantt/dashGantt.vue | 1411 +++++++++++++++++ .../src/components/faceLogin/faceLogin.vue | 54 +- .../src/components/faceLogin/tracking.vue | 53 +- hb_client/src/views/dashboard/index.vue | 503 +++--- hb_client/src/views/login/index.vue | 58 +- hb_client/src/views/mtm/materialdo.vue | 4 +- hb_client/src/views/workflow/index.vue | 8 +- hb_client/src/views/workflow/ticket.vue | 2 +- 10 files changed, 1989 insertions(+), 233 deletions(-) create mode 100644 hb_client/src/components/Gantt/components/dashLeftMenu.vue create mode 100644 hb_client/src/components/Gantt/dashGantt.vue diff --git a/hb_client/src/api/srm.js b/hb_client/src/api/srm.js index 7e3a614..ea41540 100644 --- a/hb_client/src/api/srm.js +++ b/hb_client/src/api/srm.js @@ -7,3 +7,11 @@ export function getPlanGantt(data) { params: data }) } +//合格率 +export function getProcessYield(data) { + return request({ + url: '/srm/process/yield/', + method: 'post', + data + }) +} diff --git a/hb_client/src/components/Gantt/components/dashLeftMenu.vue b/hb_client/src/components/Gantt/components/dashLeftMenu.vue new file mode 100644 index 0000000..a4d8be2 --- /dev/null +++ b/hb_client/src/components/Gantt/components/dashLeftMenu.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/hb_client/src/components/Gantt/dashGantt.vue b/hb_client/src/components/Gantt/dashGantt.vue new file mode 100644 index 0000000..e34814b --- /dev/null +++ b/hb_client/src/components/Gantt/dashGantt.vue @@ -0,0 +1,1411 @@ + + + + + diff --git a/hb_client/src/components/faceLogin/faceLogin.vue b/hb_client/src/components/faceLogin/faceLogin.vue index 269d70f..d1aaaf3 100644 --- a/hb_client/src/components/faceLogin/faceLogin.vue +++ b/hb_client/src/components/faceLogin/faceLogin.vue @@ -35,12 +35,56 @@ } }, mounted() { - this.init(); + this.openTheCamera(); }, methods: { + openTheCamera () { + this.$nextTick(function () { + let _this = this; + this.video = document.getElementById('video'); + // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象 + if (navigator.mediaDevices === undefined) { + navigator.mediaDevices = {} + } + // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象 + // 使用getUserMedia,因为它会覆盖现有的属性。 + // 这里,如果缺少getUserMedia属性,就添加它。 + if (navigator.mediaDevices.getUserMedia === undefined) { + navigator.mediaDevices.getUserMedia = function (constraints) { + // 首先获取现存的getUserMedia(如果存在) + let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia; + // 有些浏览器不支持,会返回错误信息 + // 保持接口一致 + if (!getUserMedia) { + return Promise.reject(new Error('getUserMedia is not implemented in this browser')) + } + // 否则,使用Promise将调用包装到旧的navigator.getUserMedia + return new Promise(function (resolve, reject) { + getUserMedia.call(navigator, constraints, resolve, reject) + }) + } + } + let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } }; + navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { + // 旧的浏览器可能没有srcObject + if ('srcObject' in _this.video) { + _this.video.srcObject = stream + } else { + // 避免在新的浏览器中使用它,因为它正在被弃用。 + _this.video.src = window.URL.createObjectURL(stream) + } + _this.video.onloadedmetadata = function (e) { + _this.video.play(); + }; + _this.init(); + }).catch(err => { + console.log(err) + }) + }); + }, // 初始化设置 init() { - this.video = document.getElementById('video'); + // this.video = document.getElementById('video'); this.screenshotCanvas = document.getElementById('screenshotCanvas'); let canvas = document.getElementById('canvas'); let context = canvas.getContext('2d'); @@ -64,7 +108,6 @@ event.data.forEach(function (rect) { context.strokeStyle = '#0764B7'; context.strokeRect(rect.x, rect.y, rect.width, rect.height); - // window.plot(rect.x, rect.y, rect.width, rect.height+20); // 避免重复发送请求 if(!_this.uploadLock){ _this.uploadLock = true; @@ -133,7 +176,10 @@ that.uploadLock = false; // this.$message.error('面部识别失败请重新验证'); }); - } + }, + closeCamera () { + this.video.srcObject.getTracks()[0].stop(); + }, } } diff --git a/hb_client/src/components/faceLogin/tracking.vue b/hb_client/src/components/faceLogin/tracking.vue index 67457c6..4ef978b 100644 --- a/hb_client/src/components/faceLogin/tracking.vue +++ b/hb_client/src/components/faceLogin/tracking.vue @@ -24,12 +24,55 @@ } }, mounted() { - this.init(); + this.openTheCamera(); }, methods: { + openTheCamera () { + this.$nextTick(function () { + let _this = this; + this.video = document.getElementById('video'); + // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象 + if (navigator.mediaDevices === undefined) { + navigator.mediaDevices = {} + } + // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象 + // 使用getUserMedia,因为它会覆盖现有的属性。 + // 这里,如果缺少getUserMedia属性,就添加它。 + if (navigator.mediaDevices.getUserMedia === undefined) { + navigator.mediaDevices.getUserMedia = function (constraints) { + // 首先获取现存的getUserMedia(如果存在) + let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia; + // 有些浏览器不支持,会返回错误信息 + // 保持接口一致 + if (!getUserMedia) { + return Promise.reject(new Error('getUserMedia is not implemented in this browser')) + } + // 否则,使用Promise将调用包装到旧的navigator.getUserMedia + return new Promise(function (resolve, reject) { + getUserMedia.call(navigator, constraints, resolve, reject) + }) + } + } + let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } }; + navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { + // 旧的浏览器可能没有srcObject + if ('srcObject' in _this.video) { + _this.video.srcObject = stream + } else { + // 避免在新的浏览器中使用它,因为它正在被弃用。 + _this.video.src = window.URL.createObjectURL(stream) + } + _this.video.onloadedmetadata = function (e) { + _this.video.play(); + }; + _this.init(); + }).catch(err => { + console.log(err) + }) + }); + }, // 初始化设置 init() { - this.video = document.getElementById('video'); this.screenshotCanvas = document.getElementById('screenshotCanvas'); let canvas = document.getElementById('canvas'); let context = canvas.getContext('2d'); @@ -46,7 +89,6 @@ let _this = this; //添加事件 tracker.on('track', function (event) { - // 检测出人脸 绘画人脸位置 context.clearRect(0, 0, canvas.width, canvas.height); // 给每个人脸绘制对应的框 @@ -112,7 +154,10 @@ that.uploadLock = false; // this.$message.error('面部识别失败请重新验证'); }); - } + }, + closeCamera () { + this.video.srcObject.getTracks()[0].stop(); + }, } } diff --git a/hb_client/src/views/dashboard/index.vue b/hb_client/src/views/dashboard/index.vue index 8536aa4..7f11fc3 100644 --- a/hb_client/src/views/dashboard/index.vue +++ b/hb_client/src/views/dashboard/index.vue @@ -1,58 +1,121 @@ @@ -252,11 +256,12 @@ import echarts from 'echarts' import { mapGetters } from 'vuex'; import {getPlanGantt} from "@/api/srm"; +import { getProcessYield } from "@/api/srm"; import { getMaterialList } from "@/api/mtm"; -import gantt from "@/components/Gantt/index"; import { getInventoryList } from "@/api/inm"; import { getProductionplanList} from "@/api/pm"; import { getmaterialbatchList } from "@/api/inm"; +import gantt from "@/components/Gantt/dashGantt"; import { getContractList , getOrderList } from "@/api/sam"; export default { @@ -279,7 +284,7 @@ export default { tableIndex:null, chartIndex:null, tableDate:'2021-12', - chartDate:'2021-12', + chartDate:[], proList: [], planList:[], stockList:[], @@ -299,7 +304,10 @@ export default { "6":'辅助工装', }, activeName:'库存警告', - seriesData:[80,95,96, 96, 96, 98, 99,100], + chartData:{ + xAxisData:["冷加工", "热弯", "钢化", "镀膜", "夹层", "包边", "装框"], + seriesData:[80,95,96, 96, 96, 98, 99,100], + }, contractTotalCount:null,//合同总数 contractTotalCurrent:null, orderTotalCount:null,//生产订单总数 @@ -321,9 +329,6 @@ export default { ]) }, methods:{ - openWord(){ - window.open("http://47.95.0.242:2222/media/2021/09/07/004-%E8%AF%B7%E5%81%87%E5%8D%95_LL8uZdx.docx"); - }, getStatisticsData(){ let that = this; let dat = new Date(); @@ -368,13 +373,31 @@ export default { } }); //获取交付产品 + //获取不合格产品 - getMaterialList({page:0,tag:'low_inm'}).then((response) => { + //获取成品率 + let d = new Date(that.currentYear, month, 0); + let days = d.getDate(); + let start = that.currentYear+'-'+that.currentMonth; + let startDate = start + '-01'; + let endDate = start+ '-'+days; + that.chartDate=[start,start]; + getProcessYield({datetime_start:startDate,datetime_end:endDate}).then((response) => { if (response.data) { - that.warningList = response.data; + let list = response.data; + let xAxisData = [],seriesData = []; + list.forEach(item =>{ + xAxisData.push(item.name); + let rate = item.rate*100; + seriesData.push(rate.toFixed(2)) + }); + that.chartData.xAxisData = xAxisData; + that.chartData.seriesData = seriesData; + this.drawChart(); } }); + }, gotoTicketPage(){ let path = this.$route.path; @@ -409,6 +432,7 @@ export default { }, //图标渲染 drawChart() { + let that = this; this.chartColumn = echarts.init(document.getElementById('chartColumn')); this.chartColumn.setOption({ // title: { text: '成品率' }, @@ -444,7 +468,7 @@ export default { splitLine: { show: false, //去掉X轴分割线 }, - data: ["冷加工", "热弯", "钢化", "镀膜", "夹层", "包边", "装框", "成品"] + data: that.chartData.xAxisData, }, yAxis: { axisLine:{ @@ -466,8 +490,8 @@ export default { series: [{ name: '成品率', type: 'bar', - barWidth: 40, - data: this.seriesData, + barWidth: 20, + data: that.chartData.seriesData, label: { show: true, //开启显示 position: 'top', //在上方显示 @@ -479,13 +503,13 @@ export default { }, itemStyle: { normal: { - /*color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#adcff3' }, { offset: 1, color: '#409EFF' - }]),*/ + }]), /*color: function(params) { let colorList = [ @@ -507,13 +531,13 @@ export default { } ]); },*/ - color: function(params) { + /*color: function(params) { let colorList = [ '#5fcdc2','#a378e4','#fea94c','#f27197', '#409eff','#5fcdc2','#a378e4','#fea94c' ]; return colorList[params.dataIndex] - }, + },*/ } }, }] @@ -528,7 +552,7 @@ export default { }else if(index==='2'){ this.$router.push({name:'order',params:{page:1,page_size:20}}) }else if(index==='3'){ - this.$router.push({name:'plan',params:{page:1,page_size:20}}) + this.$router.push({name:'management',params:{page:1,page_size:20}}) }else if(index==='4'){ this.$router.push({name:'product',params:{page:1,page_size:20,material__type:1}}) }else if(index==='5'){ @@ -571,18 +595,22 @@ export default { startTime = yea+'-'+mon+'-01'; } if(index==='1'){ - this.tableDate = null; - this.tableIndex = activeIndex; - this.listLoadingPlan = true; - getProductionplanList({page:0,create_time_start:startTime,create_time_end:endTime}).then((response) => { - if (response.data) { - that.planList = response.data; - } - this.listLoadingPlan = false; - }); - }else{ - this.chartDate = null; this.chartIndex = activeIndex; + getProcessYield({datetime_start:startTime,datetime_end:endTime}).then((response) => { + if (response.data) { + let list = response.data; + let xAxisData = [],seriesData = []; + list.forEach(item =>{ + xAxisData.push(item.name); + let rate = item.rate*100; + seriesData.push(rate.toFixed(2)) + }); + that.chartData.xAxisData = xAxisData; + that.chartData.seriesData = seriesData; + this.drawChart(); + } + }); + } //根据时间和类型获取数据 }, @@ -593,33 +621,34 @@ export default { let year = null,month = null,days = null; if(index==='1'){ this.tableIndex = null; - startDate = this.tableDate+'-01'; - year = this.tableDate.split("-")[0]; - month = this.tableDate.split("-")[1]; + startDate = that.chartDate[0]+'-01'; + year =that.chartDate[1].split("-")[0]; + month = that.chartDate[1].split("-")[1]; let d = new Date(year, month, 0); days = d.getDate(); endDate = this.tableDate + '-'+days; - this.listLoadingPlan = true; - getProductionplanList({page:0,create_time_start:startDate,create_time_end:endDate}).then((response) => { + getProcessYield({datetime_start:startDate,datetime_end:endDate}).then((response) => { if (response.data) { - that.planList = response.data; + let list = response.data; + let xAxisData = [],seriesData = []; + list.forEach(item =>{ + xAxisData.push(item.name); + let rate = item.rate*100; + seriesData.push(rate.toFixed(2)) + }); + that.chartData.xAxisData = xAxisData; + that.chartData.seriesData = seriesData; + this.drawChart(); } - this.listLoadingPlan = false; }); - }else{ - this.chartIndex = null; - startDate = this.chartDate+'-01'; - year = this.chartDate.split("-")[0]; - month = this.chartDate.split("-")[1]; - let d = new Date(year, month, 0); - days = d.getDate(); - endDate = this.chartDate + '-'+days; } }, + //库存 handleStockSizeChange(val){ this.stockPageSize = val; this.stockPage = 1; }, + //库存 handleStockCurrentChange(val){ let that = this; this.listLoading = true; @@ -632,6 +661,7 @@ export default { this.listLoading = false; }); }, + //提示 activeNameClick(tab, event) { let that = this; // debugger; @@ -748,11 +778,12 @@ export default { let hei = document.getElementsByClassName('app-main')[0].clientHeight; let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight; this.cardTabelHeight = ((hei-heig-130)/2); - document.getElementById('chartColumn').style.height = this.cardTabelHeight+'px'; - this.drawChart(); + document.getElementById('chartColumn').style.height = this.cardTabelHeight-35+'px'; + // this.drawChart(); this.getPlanList(); this.getStockList(); this.getGanttData(); + // this.getYield(); this.getStatisticsData(); }, updated () { @@ -768,70 +799,109 @@ export default { .el-card.is-always-shadow{ height: auto!important; } -.dashboard-container { - margin: 5px 6px; -} -.dashboard-text { - font-size: 30px; - line-height: 46px; -} -.dashboardTopCard{ - margin-bottom: 5px; - border-radius: 10px; - .cards{ - width: 18%; - color: #ffffff; - background: #5fcdc2; - text-align: center; - border-radius: 10px; - padding: 10px; - line-height: 30px; + .dashboard-container { + margin: 5px 6px; + } + .dashboardTopCard,.dashboardMiddle{ + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + margin-bottom: 5px; + background: #ffffff; + box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); + } +/**/ + .cardsWrap{ + display: flex; + width: 20%; + color: #777777; float: left; - margin: 15px 1%; - .cardCurrentText{ - text-align: left; - padding-left: 20px; - } - .cardTotalText{ - font-size: 30px; + font-size: 14px; + } + .svgIconWrap{ + margin-right:20px; + width: 50px; + height: 50px; + border-radius: 15px; + text-align: center; + .svgIcon{ + font-size:24px; + margin-top: 13px } } - .cards:nth-of-type(2){ - background:#5fcdc2;; + .cardsWrap:nth-child(1){ + .svgIconWrap{ + background:#e9f3ff; + .svgIcon{ + color: #409EFF; + } + } } - .cards:nth-child(3){ - background:#a378e4;; + .cardsWrap:nth-child(2){ + .svgIconWrap{ + background:#fff1de; + .svgIcon{ + color: #ffb23f; + } + } } - .cards:nth-of-type(4){ - background:#fea94c;; + + .cardsWrap:nth-child(3){ + .svgIconWrap{ + background:#d9f6d8; + .svgIcon{ + color: #54cb48; + } + } } - .cards:nth-of-type(5){ - background:#f27197;; + .cardsWrap:nth-child(4){ + .svgIconWrap{ + background:#f0e8fd; + .svgIcon{ + color: #a378e4; + } + } } - .cards:nth-of-type(6){ - background:#409eff;; + .cardsWrap:nth-child(5){ + .svgIconWrap{ + background:#f7e5ea; + .svgIcon{ + color: #f27197; + } + } } - .cards:hover{ - box-shadow: 0 0 12px 5px rgba(0,0,0,.1); + .totalCountText{ + height: 20px; + line-height: 20px } -} + .totalCountNum{ + height: 30px; + } + .totalCount{ + font-size: 25px; + font-weight: bold; + color: #626262; + } + /**/ .CardTitleWrap{ display: flex; height: 35px; line-height: 35px; padding-left: 1%; - padding-top: 6px; + box-sizing: border-box; + border-bottom: 1px solid #f5f5f5; + /*padding-top: 6px;*/ .verticalLine{ width: 4px; - height: 20px; - background: orangered; + height: 15px; + background: #409EFF; margin-right: 7px; - margin-top: 6px; + margin-top: 8px; } .dashboardCardTitle{ - height: 35px; - line-height:35px; - font-size: 20px; + height: 34px; + line-height:34px; + font-size: 14px; font-weight: bold; vertical-align: middle; margin-right: 7px; @@ -843,9 +913,7 @@ export default { cursor: pointer; } } - -.dashboardSubRow{ - margin-bottom: 5px; +/*成品率筛选条件*/ .dashboardCardHand{ display: flex; justify-content: space-between; @@ -855,12 +923,13 @@ export default { display: flex; border: 1px solid #DCDFE6; border-radius: 6px; - height: 35px; - line-height:35px; + height: 30px; + line-height:30px; margin-left: 10px; + font-size: 12px; .convenientBtn{ cursor: pointer; - width: 60px; + width: 50px; text-align:center; border-right: 1px solid #DCDFE6; } @@ -880,8 +949,6 @@ export default { } -} - .lists{ padding-right: 40px; .listItem{ @@ -900,4 +967,8 @@ export default { height: 100%!important; } } + #dashboardMiddle .el-range-editor--medium.el-input__inner, + #dashboardMiddle .el-range-editor.el-input__inner{ + height: 30px!important; + } diff --git a/hb_client/src/views/login/index.vue b/hb_client/src/views/login/index.vue index 4f256c6..53da72e 100644 --- a/hb_client/src/views/login/index.vue +++ b/hb_client/src/views/login/index.vue @@ -64,10 +64,10 @@ - +
打卡
- +
@@ -179,9 +179,61 @@ //人脸登录 takePhoto(){ this.limitedPhoto = true; + this.openTheCamera(); + }, + /*打开相机*/ + openTheCamera () { + this.$nextTick(function () { + let _this = this; + this.thisVideo = document.getElementById('videoCamera'); + // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象 + if (navigator.mediaDevices === undefined) { + navigator.mediaDevices = {} + } + // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象 + // 使用getUserMedia,因为它会覆盖现有的属性。 + // 这里,如果缺少getUserMedia属性,就添加它。 + if (navigator.mediaDevices.getUserMedia === undefined) { + navigator.mediaDevices.getUserMedia = function (constraints) { + // 首先获取现存的getUserMedia(如果存在) + let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia; + // 有些浏览器不支持,会返回错误信息 + // 保持接口一致 + if (!getUserMedia) { + return Promise.reject(new Error('getUserMedia is not implemented in this browser')) + } + // 否则,使用Promise将调用包装到旧的navigator.getUserMedia + return new Promise(function (resolve, reject) { + getUserMedia.call(navigator, constraints, resolve, reject) + }) + } + } + + let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } }; + navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { + // 旧的浏览器可能没有srcObject + if ('srcObject' in _this.thisVideo) { + _this.thisVideo.srcObject = stream + } else { + // 避免在新的浏览器中使用它,因为它正在被弃用。 + _this.thisVideo.src = window.URL.createObjectURL(stream) + } + _this.thisVideo.onloadedmetadata = function (e) { + _this.thisVideo.play(); + } + }).catch(err => { + console.log(err) + }) + }); + }, + /*关闭相机*/ + closeCamera () { + debugger; + this.$refs.faceTracking.closeCamera(); + // this.thisVideo.srcObject.getTracks()[0].stop(); }, getMsgFormSon(data){ - this.limitedPhoto = data; + // this.limitedPhoto = data; }, }, }; diff --git a/hb_client/src/views/mtm/materialdo.vue b/hb_client/src/views/mtm/materialdo.vue index 19b98e6..b42acd3 100644 --- a/hb_client/src/views/mtm/materialdo.vue +++ b/hb_client/src/views/mtm/materialdo.vue @@ -82,8 +82,8 @@
取消 - - 管理员授权 + 确认 +
diff --git a/hb_client/src/views/workflow/index.vue b/hb_client/src/views/workflow/index.vue index 5a57a88..1694c63 100644 --- a/hb_client/src/views/workflow/index.vue +++ b/hb_client/src/views/workflow/index.vue @@ -102,8 +102,10 @@

创建时间 :{{watchedCreateTime}}

- - +
+ + +
- +
From 3023929d665dcaa1f8375a9b1cb6737ae2a65043 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 20 Jan 2022 15:47:05 +0800 Subject: [PATCH 22/34] =?UTF-8?q?iproduct=E5=A2=9E=E5=8A=A0to=5Forder?= =?UTF-8?q?=E7=AD=9B=E9=80=89=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/filters.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/inm/filters.py b/hb_server/apps/inm/filters.py index bc97441..73f92a6 100644 --- a/hb_server/apps/inm/filters.py +++ b/hb_server/apps/inm/filters.py @@ -21,8 +21,10 @@ class MbFilterSet(filters.FilterSet): class IProductFilterSet(filters.FilterSet): order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order") + to_order = filters.NumberFilter(field_name="wproduct__to_order") update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte') update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte') class Meta: model = IProduct - fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'is_saled', 'update_time_start', 'update_time_end'] + fields = ['material', 'warehouse', 'batch', 'order', 'material__type', + 'is_saled', 'update_time_start', 'update_time_end', 'to_order'] From d1270c16abf826c5e2c9d5e36b5a1184852825f7 Mon Sep 17 00:00:00 2001 From: shijing Date: Fri, 21 Jan 2022 08:30:22 +0800 Subject: [PATCH 23/34] newStyle --- .../Gantt/components/dashLeftMenu.vue | 7 +++- hb_client/src/components/Gantt/dashGantt.vue | 38 +++++++++++-------- hb_client/src/views/dashboard/index.vue | 12 +++--- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/hb_client/src/components/Gantt/components/dashLeftMenu.vue b/hb_client/src/components/Gantt/components/dashLeftMenu.vue index a4d8be2..9014840 100644 --- a/hb_client/src/components/Gantt/components/dashLeftMenu.vue +++ b/hb_client/src/components/Gantt/components/dashLeftMenu.vue @@ -8,9 +8,9 @@ diff --git a/hb_client/src/views/mtm/material.vue b/hb_client/src/views/mtm/material.vue index 0caa4fb..384a3fd 100644 --- a/hb_client/src/views/mtm/material.vue +++ b/hb_client/src/views/mtm/material.vue @@ -121,7 +121,7 @@ @@ -131,41 +131,42 @@ - + - + + :value="item.value" + > - - - + + + :value="item.value" + > - + - - - - + + + + + -
取消 @@ -189,8 +190,14 @@ import {genTree} from "@/utils"; import Pagination from "@/components/Pagination"; // secondary package based on el-pagination const defaultmaterial = { - name: "", - number: "", + name: null, + number: null, + specification: null, + unit: null, + type: null, + piece_count: null, + sort_str: null, + count_safe: 0, processes: [], }; export default { @@ -270,19 +277,13 @@ created() { this.getList(); this.getProcessList(); - }, methods: { checkPermission, - //物料详情 - handledetail(scope) - { + handledetail(scope){ this.$router.push({name: "MaterialDetail", params: { id: scope.row.id,type: scope.row.type }, }) - }, - - //选项卡切换 handleClick(tab) { this.listLoading = true; @@ -311,10 +312,8 @@ }, //绑定工序 handlebind(scope) { - this.$router.push({name: "MaterialDO", params: {id: scope.row.id},}) - } - , + }, handleFilter() { this.listQuery.page = 1; this.getList(); @@ -324,7 +323,7 @@ this.listQuery = { page: 1, page_size: 20, - } + }; this.getList(); }, handleCreate() { @@ -335,7 +334,6 @@ this.$refs["Form"].clearValidate(); }); }, - handleEdit(scope) { this.material = Object.assign({}, scope.row); // copy obj this.dialogType = "edit"; @@ -359,7 +357,6 @@ console.error(err); }); }, - async confirm(form) { this.$refs[form].validate((valid) => { if (valid) { From 509404994a54bf56f4abe3ccccaea68902271a7e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 13:41:54 +0800 Subject: [PATCH 27/34] =?UTF-8?q?=E6=89=93=E5=8D=A1=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/hrm/filters.py | 9 ++ hb_server/apps/hrm/models.py | 14 ++- hb_server/apps/hrm/serializers.py | 12 +- hb_server/apps/hrm/services.py | 37 ++++++ hb_server/apps/hrm/urls.py | 3 +- hb_server/apps/hrm/views.py | 106 ++++++++++-------- hb_server/apps/inm/serializers.py | 1 + .../system/migrations/0004_user_is_atwork.py | 18 +++ hb_server/apps/system/models.py | 1 + 9 files changed, 150 insertions(+), 51 deletions(-) create mode 100644 hb_server/apps/hrm/filters.py create mode 100644 hb_server/apps/hrm/services.py create mode 100644 hb_server/apps/system/migrations/0004_user_is_atwork.py diff --git a/hb_server/apps/hrm/filters.py b/hb_server/apps/hrm/filters.py new file mode 100644 index 0000000..40c9b45 --- /dev/null +++ b/hb_server/apps/hrm/filters.py @@ -0,0 +1,9 @@ +from django_filters import rest_framework as filters +from apps.hrm.models import ClockRecord + +class ClockRecordFilterSet(filters.FilterSet): + create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte') + create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte') + class Meta: + model = ClockRecord + fields = ['create_by', 'create_time_start', 'create_time_end'] \ No newline at end of file diff --git a/hb_server/apps/hrm/models.py b/hb_server/apps/hrm/models.py index 5f95387..490237e 100644 --- a/hb_server/apps/hrm/models.py +++ b/hb_server/apps/hrm/models.py @@ -36,8 +36,12 @@ class Employee(CommonAModel): def __str__(self): return self.name -# class Attendance(CommonADModel): -# """ -# 出勤记录 -# """ - \ No newline at end of file +class ClockRecord(CommonADModel): + """ + 打卡记录 + """ + ClOCK_WORK1 = 10 + type_choice = ( + (ClOCK_WORK1, '上班打卡'), + ) + type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1) \ No newline at end of file diff --git a/hb_server/apps/hrm/serializers.py b/hb_server/apps/hrm/serializers.py index 7a14400..429cc40 100644 --- a/hb_server/apps/hrm/serializers.py +++ b/hb_server/apps/hrm/serializers.py @@ -1,7 +1,7 @@ from apps.system.models import User from rest_framework.serializers import ModelSerializer from rest_framework import serializers -from .models import Employee +from .models import ClockRecord, Employee from apps.system.serializers import UserListSerializer, UserSimpleSerializer from django.db.models.query import Prefetch @@ -23,3 +23,13 @@ class EmployeeSerializer(ModelSerializer): class FaceLoginSerializer(serializers.Serializer): base64 = serializers.CharField() + + +class FaceClockCreateSerializer(serializers.Serializer): + base64 = serializers.CharField() + +class ClockRecordListSerializer(serializers.ModelSerializer): + create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + class Meta: + model = ClockRecord + fields = '__all__' diff --git a/hb_server/apps/hrm/services.py b/hb_server/apps/hrm/services.py new file mode 100644 index 0000000..14f5eb1 --- /dev/null +++ b/hb_server/apps/hrm/services.py @@ -0,0 +1,37 @@ +from django.conf import settings +import uuid +import face_recognition +import os +from apps.hrm.models import Employee +from apps.system.models import User + +class HRMService: + + def face_compare_from_base64(cls, base64_data): + filename = str(uuid.uuid4()) + filepath = settings.BASE_DIR +'/temp/' + filename +'.png' + with open(filepath, 'wb') as f: + f.write(base64_data) + try: + unknown_picture = face_recognition.load_image_file(filepath) + unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0] + os.remove(filepath) + except: + os.remove(filepath) + return None, '头像解码失败' + + # 匹配人脸库 + user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data') + user_l = [] + face_l = [] + for i in user_faces: + user_l.append(i['user']) + face_l.append(i['face_data']) + + results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5) + for index, value in enumerate(results): + if value: + # 识别成功 + user = User.objects.get(id=user_l[index]) + return user, '' + return None, '识别失败' \ No newline at end of file diff --git a/hb_server/apps/hrm/urls.py b/hb_server/apps/hrm/urls.py index 293124c..55c2e55 100644 --- a/hb_server/apps/hrm/urls.py +++ b/hb_server/apps/hrm/urls.py @@ -1,11 +1,12 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.hrm.views import EmployeeViewSet, FaceLogin +from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin from django.urls import path, include from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('employee', EmployeeViewSet, basename='employee') +router.register('clock_record', ClockRecordViewSet, basename='clock_record') urlpatterns = [ path('facelogin/', FaceLogin.as_view()), path('', include(router.urls)), diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index e4b7b3e..0f87cd3 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -1,10 +1,13 @@ from django.shortcuts import render +from pytz import timezone from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, GenericViewSet -from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin +from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin +from apps.hrm.filters import ClockRecordFilterSet +from apps.hrm.services import HRMService from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin -from apps.hrm.models import Employee -from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer +from apps.hrm.models import ClockRecord, Employee +from apps.hrm.serializers import ClockRecordListSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer import face_recognition from django.conf import settings from django.core.cache import cache @@ -12,8 +15,9 @@ import logging from rest_framework.generics import CreateAPIView from rest_framework import status from rest_framework_simplejwt.tokens import RefreshToken - +from rest_framework import exceptions from apps.system.models import User +from apps.system.serializers import UserSimpleSerializer logger = logging.getLogger('log') @@ -50,9 +54,51 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix except: logger.error('人脸识别出错') -import uuid + +class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): + """ + 打卡记录 + """ + perms_map = {'get':'*', 'post':'*'} + authentication_classes = [] + queryset = ClockRecord.objects.select_related('create_by').all() + serializer_class = ClockRecordListSerializer + filterset_class = ClockRecordFilterSet + ordering = ['-pk'] + + def get_serializer_class(self): + if self.action == 'create': + return FaceClockCreateSerializer + return super().get_serializer_class() + + def create(self, request, *args, **kwargs): + base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+'))) + user, msg = HRMService.face_compare_from_base64(base64_data) + if user: + now = timezone.now() + if 7<=now.hour<=9: + ins, created = ClockRecord.objects.get_or_create( + create_by=request.user, create_time__hour__in = [7,8], + create_time__year=now.year, create_time__month=now.month, create_time__day=now.day, + defaults={ + 'type':ClockRecord.ClOCK_WORK1, + 'create_by':user, + 'create_time':now + }) + if not created: + ins.create_time = now + ins.save() + # 设为在岗 + user.is_atwork = True + user.save() + return Response(UserSimpleSerializer(instance=user).data) + return Response('打卡失败', status=status.HTTP_400_BAD_REQUEST) + return Response(msg, status=status.HTTP_400_BAD_REQUEST) + + + + import base64 -import os def tran64(s): missing_padding = len(s) % 4 @@ -70,41 +116,13 @@ class FaceLogin(CreateAPIView): """ 人脸识别登录 """ - # serializer = FaceLoginSerializer(data=request.data) - # serializer.is_valid(raise_exception=True) - filename = str(uuid.uuid4()) - filepath = settings.BASE_DIR +'/temp/' + filename +'.png' - with open(filepath, 'wb') as f: - data = tran64(request.data.get('base64').replace(' ', '+')) - f.write(base64.urlsafe_b64decode(data)) - # picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png') - # my_face_encoding = face_recognition.face_encodings(picture_of_me)[0] - #results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2) - try: - unknown_picture = face_recognition.load_image_file(filepath) - unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0] - os.remove(filepath) - except: - os.remove(filepath) - return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST) - - # 匹配人脸库 - user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data') - user_l = [] - face_l = [] - for i in user_faces: - user_l.append(i['user']) - face_l.append(i['face_data']) - - results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5) - for index, value in enumerate(results): - if value: - # 识别成功 - user = User.objects.get(id=user_l[index]) - refresh = RefreshToken.for_user(user) - return Response({ - 'refresh': str(refresh), - 'access': str(refresh.access_token), - 'username':user.username - }) - return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+'))) + user, msg = HRMService.face_compare_from_base64(base64_data) + if user: + refresh = RefreshToken.for_user(user) + return Response({ + 'refresh': str(refresh), + 'access': str(refresh.access_token), + 'username':user.username + }) + return Response(msg, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index e983108..766a164 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -9,6 +9,7 @@ from apps.mtm.serializers import MaterialSimpleSerializer from django.db import transaction + class WareHouseSerializer(serializers.ModelSerializer): create_by_ = UserSimpleSerializer('create_by', read_only=True) diff --git a/hb_server/apps/system/migrations/0004_user_is_atwork.py b/hb_server/apps/system/migrations/0004_user_is_atwork.py new file mode 100644 index 0000000..b512387 --- /dev/null +++ b/hb_server/apps/system/migrations/0004_user_is_atwork.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-01-21 05:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0003_auto_20210812_0909'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='is_atwork', + field=models.BooleanField(default=False, verbose_name='当前在岗'), + ), + ] diff --git a/hb_server/apps/system/models.py b/hb_server/apps/system/models.py index c773fcf..e77eae3 100644 --- a/hb_server/apps/system/models.py +++ b/hb_server/apps/system/models.py @@ -116,6 +116,7 @@ class User(AbstractUser): superior = models.ForeignKey( 'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管') roles = models.ManyToManyField(Role, blank=True, verbose_name='角色') + is_atwork = models.BooleanField('当前在岗', default=False) class Meta: verbose_name = '用户信息' From ad0599ec66ce2979538e2a0ddc071f5041abac53 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 13:45:57 +0800 Subject: [PATCH 28/34] =?UTF-8?q?=E6=89=93=E5=8D=A1=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=E8=AE=BE=E7=BD=AE=E5=AE=BD,=E4=BB=A5?= =?UTF-8?q?=E4=BE=9B=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/hrm/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 0f87cd3..fb49034 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -76,9 +76,9 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): user, msg = HRMService.face_compare_from_base64(base64_data) if user: now = timezone.now() - if 7<=now.hour<=9: + if 8<=now.hour<=17: ins, created = ClockRecord.objects.get_or_create( - create_by=request.user, create_time__hour__in = [7,8], + create_by=request.user, create_time__hour__range = [8,18], create_time__year=now.year, create_time__month=now.month, create_time__day=now.day, defaults={ 'type':ClockRecord.ClOCK_WORK1, @@ -92,7 +92,7 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): user.is_atwork = True user.save() return Response(UserSimpleSerializer(instance=user).data) - return Response('打卡失败', status=status.HTTP_400_BAD_REQUEST) + return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST) return Response(msg, status=status.HTTP_400_BAD_REQUEST) From 2261cf162761b83ae07a092c95161fce53bd9fed Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 13:49:18 +0800 Subject: [PATCH 29/34] =?UTF-8?q?user=20list=20=E5=A2=9E=E5=8A=A0is=5Fatwo?= =?UTF-8?q?rk=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/system/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index 10636ea..8d644b3 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -141,7 +141,9 @@ class UserListSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'name', 'phone', 'email', 'position', - 'username', 'is_active', 'date_joined', 'dept_name', 'dept', 'roles', 'avatar', 'roles_name'] + 'username', 'is_active', 'date_joined', + 'dept_name', 'dept', 'roles', 'avatar', + 'roles_name', 'is_atwork'] @staticmethod def setup_eager_loading(queryset): From 988dc29ef6e6a484dd924eb7e6a45371253d8d0b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 13:59:38 +0800 Subject: [PATCH 30/34] clock record post perm --- hb_server/apps/hrm/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index fb49034..6b83098 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -18,6 +18,7 @@ from rest_framework_simplejwt.tokens import RefreshToken from rest_framework import exceptions from apps.system.models import User from apps.system.serializers import UserSimpleSerializer +from rest_framework.permissions import AllowAny logger = logging.getLogger('log') @@ -61,6 +62,7 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): """ perms_map = {'get':'*', 'post':'*'} authentication_classes = [] + permission_classes = [AllowAny] queryset = ClockRecord.objects.select_related('create_by').all() serializer_class = ClockRecordListSerializer filterset_class = ClockRecordFilterSet From 7b81a34d8930dfa8284b4827d3e4c7b2653588d6 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 14:04:39 +0800 Subject: [PATCH 31/34] face cinoare from base64 bug --- hb_server/apps/hrm/services.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hb_server/apps/hrm/services.py b/hb_server/apps/hrm/services.py index 14f5eb1..7cdc013 100644 --- a/hb_server/apps/hrm/services.py +++ b/hb_server/apps/hrm/services.py @@ -7,6 +7,7 @@ from apps.system.models import User class HRMService: + @classmethod def face_compare_from_base64(cls, base64_data): filename = str(uuid.uuid4()) filepath = settings.BASE_DIR +'/temp/' + filename +'.png' From 832f58d35febfcf1ac8673a8017a453678a1c934 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 14:16:31 +0800 Subject: [PATCH 32/34] clock record bug --- hb_server/apps/hrm/services.py | 3 ++- hb_server/apps/hrm/views.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/hrm/services.py b/hb_server/apps/hrm/services.py index 7cdc013..a80cb8e 100644 --- a/hb_server/apps/hrm/services.py +++ b/hb_server/apps/hrm/services.py @@ -22,7 +22,8 @@ class HRMService: return None, '头像解码失败' # 匹配人脸库 - user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data') + user_faces = Employee.objects.filter(face_data__isnull=False, + user__is_active=True).values('user', 'face_data') user_l = [] face_l = [] for i in user_faces: diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 6b83098..ca60ee6 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render -from pytz import timezone +from django.utils import timezone from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin From f30c2b974131096c365a47330cc179ab1962d57f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 14:43:09 +0800 Subject: [PATCH 33/34] =?UTF-8?q?=E6=89=93=E5=8D=A1=E8=AE=B0=E5=BD=95=20ti?= =?UTF-8?q?mzone=20localtime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/hrm/tasks.py | 8 ++++++++ hb_server/apps/hrm/views.py | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 hb_server/apps/hrm/tasks.py diff --git a/hb_server/apps/hrm/tasks.py b/hb_server/apps/hrm/tasks.py new file mode 100644 index 0000000..bd4449b --- /dev/null +++ b/hb_server/apps/hrm/tasks.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import, unicode_literals + +from celery import shared_task + + +@shared_task +def x(): + print('ok') \ No newline at end of file diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index ca60ee6..10b4b06 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -74,14 +74,16 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): return super().get_serializer_class() def create(self, request, *args, **kwargs): - base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+'))) - user, msg = HRMService.face_compare_from_base64(base64_data) - if user: - now = timezone.now() - if 8<=now.hour<=17: + now = timezone.localtime() + if 8<=now.hour<=17: + base64_data = base64.urlsafe_b64decode(tran64( + request.data.get('base64').replace(' ', '+'))) + user, msg = HRMService.face_compare_from_base64(base64_data) + if user: ins, created = ClockRecord.objects.get_or_create( create_by=request.user, create_time__hour__range = [8,18], - create_time__year=now.year, create_time__month=now.month, create_time__day=now.day, + create_time__year=now.year, create_time__month=now.month, + create_time__day=now.day, defaults={ 'type':ClockRecord.ClOCK_WORK1, 'create_by':user, @@ -94,8 +96,9 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): user.is_atwork = True user.save() return Response(UserSimpleSerializer(instance=user).data) - return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST) - return Response(msg, status=status.HTTP_400_BAD_REQUEST) + return Response(msg, status=status.HTTP_400_BAD_REQUEST) + return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST) + From db87609ad6cfaf1579fa531bc0e763050812d94c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 21 Jan 2022 14:46:02 +0800 Subject: [PATCH 34/34] =?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 --- .../apps/hrm/migrations/0004_clockrecord.py | 32 +++++++++++++++++++ hb_server/apps/hrm/views.py | 5 +-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 hb_server/apps/hrm/migrations/0004_clockrecord.py diff --git a/hb_server/apps/hrm/migrations/0004_clockrecord.py b/hb_server/apps/hrm/migrations/0004_clockrecord.py new file mode 100644 index 0000000..97ae889 --- /dev/null +++ b/hb_server/apps/hrm/migrations/0004_clockrecord.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.9 on 2022-01-21 06:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('hrm', '0003_employee_face_data'), + ] + + operations = [ + migrations.CreateModel( + name='ClockRecord', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index 10b4b06..9c533d0 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -74,8 +74,9 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): return super().get_serializer_class() def create(self, request, *args, **kwargs): - now = timezone.localtime() - if 8<=now.hour<=17: + now = timezone.now() + now_local = timezone.localtime() + if 8<=now_local.hour<=17: base64_data = base64.urlsafe_b64decode(tran64( request.data.get('base64').replace(' ', '+'))) user, msg = HRMService.face_compare_from_base64(base64_data)