diff --git a/hb_server/apps/develop/views.py b/hb_server/apps/develop/views.py index ec43e41..f46227b 100644 --- a/hb_server/apps/develop/views.py +++ b/hb_server/apps/develop/views.py @@ -121,6 +121,10 @@ class UpdateFIFONumber(APIView): i.save() return Response() +class CorrectWproductState(APIView): + permission_classes = [IsAdminUser] + def post(self, request): + pass class ReloadServer(APIView): diff --git a/hb_server/apps/em/migrations/0013_alter_equipment_state.py b/hb_server/apps/em/migrations/0013_alter_equipment_state.py new file mode 100644 index 0000000..da0e59b --- /dev/null +++ b/hb_server/apps/em/migrations/0013_alter_equipment_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-03-15 07:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0012_auto_20220120_1048'), + ] + + operations = [ + migrations.AlterField( + model_name='equipment', + name='state', + field=models.PositiveIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用'), (50, '报废')], default=0, verbose_name='设备状态'), + ), + ] diff --git a/hb_server/apps/inm/migrations/0034_alter_fifo_sale.py b/hb_server/apps/inm/migrations/0034_alter_fifo_sale.py new file mode 100644 index 0000000..4bd942d --- /dev/null +++ b/hb_server/apps/inm/migrations/0034_alter_fifo_sale.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.9 on 2022-03-15 07:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sam', '0016_sale_iproducts'), + ('inm', '0033_fifoitem_expiration_date'), + ] + + operations = [ + migrations.AlterField( + model_name='fifo', + name='sale', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fifo_sale', to='sam.sale', verbose_name='关联销售记录'), + ), + ] diff --git a/hb_server/apps/srm/serializers.py b/hb_server/apps/srm/serializers.py index 283d360..ab5e1a4 100644 --- a/hb_server/apps/srm/serializers.py +++ b/hb_server/apps/srm/serializers.py @@ -1,6 +1,8 @@ +from email.policy import default from rest_framework import serializers from apps.pm.models import ProductionPlan, SubProductionPlan from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer +from apps.system.models import Organization class SubplanGanttSerializer(serializers.ModelSerializer): process_ = ProcessSimpleSerializer(source='process', read_only=True) @@ -25,10 +27,20 @@ class ProcessYieldSerializer(serializers.Serializer): datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True) datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True) -class ProductCountSerializer(serializers.Serializer): +class SrmCountSerializer(serializers.Serializer): datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True) datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True) +class ProductCountSerializer(serializers.Serializer): + tag_choices=( + (1, '统计成品'), + (2, '统计全部') + ) + datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True) + datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True) + tag = serializers.ChoiceField(choices=tag_choices, label='统计范围1成品2全部', default=1) + dept = serializers.PrimaryKeyRelatedField(queryset=Organization.objects.all(), label="车间", required=False) + class AtWorkCountSerializer(serializers.Serializer): year = serializers.IntegerField(label='年') month = serializers.IntegerField(label='月') \ No newline at end of file diff --git a/hb_server/apps/srm/services.py b/hb_server/apps/srm/services.py index 2c3400b..e6edf02 100644 --- a/hb_server/apps/srm/services.py +++ b/hb_server/apps/srm/services.py @@ -1,25 +1,68 @@ from apps.mtm.models import Material +from apps.pm.models import ProductionPlan +from apps.sam.models import Order from apps.wpm.models import WProduct, WproductFlow - +from django.db.models import F class SrmServices: """ 数据统计分析 """ @classmethod - def get_wp_product_count(cls, datetime_start, datetime_end): + def get_product_count(cls, datetime_start=None, datetime_end=None, tag=1, dept=None): """ 根据生产情况统计相关数量 """ - objs = WproductFlow.objects.filter(is_lastlog=True, material__type=Material.MA_TYPE_GOOD) + if tag == 1: + objs = WproductFlow.objects.filter(is_lastlog=True, material__type=Material.MA_TYPE_GOOD) + else: + objs = WproductFlow.objects.filter(is_lastlog=True) if datetime_start: objs = objs.filter(create_time__gte=datetime_start) if datetime_end: objs = objs.filter(create_time__lte=datetime_end) + if dept: + objs = objs.filter(subproduction_plan__workshop=dept) count = objs.count() 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]).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_selled = objs.filter(act_state=WProduct.WPR_ACT_STATE_SELLED).count() count_mtestok = objs.filter(is_mtestok=True).count() - return dict(count=count,count_ok=count_ok, count_notok=count_notok, count_selled=count_selled, count_mtestok=count_mtestok) \ No newline at end of file + return dict(count=count,count_ok=count_ok, count_notok=count_notok, count_selled=count_selled, count_mtestok=count_mtestok) + + @classmethod + def get_plan_count(cls, datetime_start=None, datetime_end=None): + """ + 任务数量 + """ + objs = ProductionPlan.objects.all() + if datetime_start: + objs = objs.filter(end_date__gte=datetime_start) + if datetime_end: + objs = objs.filter(end_date__lte=datetime_end) + count = objs.count() + count_use = objs.exclude(state__in=[ProductionPlan.PLAN_STATE_PAUSE, ProductionPlan.PLAN_STATE_STOP]).count() + count_completed = objs.filter(state__in=[ProductionPlan.PLAN_STATE_DONE, ProductionPlan.PLAN_MTEST_DONE]).count() + return dict(count=count, count_use=count_use, count_completed=count_completed) + + + @classmethod + def get_order_count(cls, datetime_start=None, datetime_end=None): + """ + 订单数量 + """ + objs = Order.objects.all() + if datetime_start: + objs = objs.filter(delivery_date__gte=datetime_start) + if datetime_end: + objs = objs.filter(delivery_date__lte=datetime_end) + count = objs.count() + count_delivered = objs.filter(delivered_count__gte=F('count')).count() + return dict(count=count, count_delivered=count_delivered) + \ No newline at end of file diff --git a/hb_server/apps/srm/urls.py b/hb_server/apps/srm/urls.py index 2437fbe..2829723 100644 --- a/hb_server/apps/srm/urls.py +++ b/hb_server/apps/srm/urls.py @@ -3,13 +3,16 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.srm.views import AtWorkCountView, GanttPlan, ProcessYieldView, ProductCountView +from apps.srm.views import AtWorkCountView, GanttPlan, OrderCountView, PlanCountView, ProcessNowView, ProcessYieldView, ProductCountView router = DefaultRouter() urlpatterns = [ path('gantt/plan/', GanttPlan.as_view()), path('product/count/', ProductCountView.as_view()), + path('plan/count/', PlanCountView.as_view()), + path('order/count/', OrderCountView.as_view()), path('process/yield/', ProcessYieldView.as_view()), + path('process/now/', ProcessNowView.as_view()), path('at_work/', AtWorkCountView.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index 28d0da3..158489f 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -1,6 +1,7 @@ from datetime import date, timedelta from django.shortcuts import render +from idna import valid_contextj from numpy import number from rest_framework import serializers from rest_framework.generics import ListAPIView, CreateAPIView @@ -9,10 +10,10 @@ from rest_framework.response import Response from apps.hrm.models import ClockRecord from apps.mtm.models import Process, Step from apps.pm.models import ProductionPlan, SubProductionPlan -from apps.srm.serializers import AtWorkCountSerializer, PlanGanttSerializer, ProcessYieldSerializer, ProductCountSerializer +from apps.srm.serializers import AtWorkCountSerializer, PlanGanttSerializer, ProcessYieldSerializer, ProductCountSerializer, SrmCountSerializer from apps.srm.services import SrmServices from apps.wpm.models import WProduct, WproductFlow -from django.db.models import Count, F +from django.db.models import Count, F, Sum # Create your views here. class GanttPlan(ListAPIView): @@ -34,9 +35,52 @@ class ProductCountView(CreateAPIView): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data - res = SrmServices.get_wp_product_count(datetime_start= vdata.get('datetime_start', None), datetime_end= vdata.get('datetime_end', None)) + res = SrmServices.get_product_count(**vdata) return Response(res) + +class PlanCountView(CreateAPIView): + """ + 计划数量统计 + """ + perms_map = {'post':'*'} + serializer_class = SrmCountSerializer + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + res = SrmServices.get_plan_count(**vdata) + return Response(res) + +class OrderCountView(CreateAPIView): + """ + 订单数量统计 + """ + perms_map = {'post':'*'} + serializer_class = SrmCountSerializer + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + res = SrmServices.get_order_count(**vdata) + return Response(res) + +class ProcessNowView(CreateAPIView): + """ + 工序当前进度 + """ + perms_map = {'post':'*'} + serializers_class = serializers.Serializer + + def create(self, request, *args, **kwargs): + objs = SubProductionPlan.objects.filter(production_plan__state__in =[ProductionPlan.PLAN_STATE_WORKING, + ProductionPlan.PLAN_STATE_ASSGINED]).order_by('process__number').values('process', + 'process__name').annotate(count_ok=Sum('count_ok'), + count=Sum('count'), count_real=Sum('count_real'), count_notok=Sum('count_notok')) + return Response(objs) + class ProcessYieldView(CreateAPIView): """ 工序成品率统计 diff --git a/hb_server/apps/wpm/migrations/0054_auto_20220315_1500.py b/hb_server/apps/wpm/migrations/0054_auto_20220315_1500.py new file mode 100644 index 0000000..3b4a060 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0054_auto_20220315_1500.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.9 on 2022-03-15 07:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0053_auto_20220129_1512'), + ] + + operations = [ + migrations.AlterField( + model_name='operationequip', + name='state', + field=models.PositiveSmallIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用'), (50, '报废')], default=10, verbose_name='当前设备状态'), + ), + migrations.AlterField( + model_name='wproduct', + name='act_state', + field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出'), (90, '已使用')], default=0, verbose_name='进行状态'), + ), + migrations.AlterField( + model_name='wproductflow', + name='act_state', + field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出'), (90, '已使用')], default=0, verbose_name='进行状态'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index f75908f..b408f18 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -43,6 +43,7 @@ class WProduct(CommonAModel): WPR_ACT_STATE_TOFINALTEST = 60 WPR_ACT_STATE_SCRAP = 70 WPR_ACT_STATE_SELLED = 80 + WPR_ACT_STATE_USED = 90 act_state_choices = ( (WPR_ACT_STATE_TORETEST, '待复检'), (WPR_ACT_STATE_DOWAIT, '操作准备中'), @@ -55,6 +56,7 @@ class WProduct(CommonAModel): (WPR_ACT_STATE_TOFINALTEST, '待成品检验'), (WPR_ACT_STATE_SCRAP, '已报废'), (WPR_ACT_STATE_SELLED, '已售出'), + (WPR_ACT_STATE_USED, '已使用'), ) SCRAP_REASON_QIPAO = 10 SCRAP_REASON_PODIAN = 20 diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 430dc78..b8234d0 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -781,8 +781,9 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd WpmService.add_wproduct_flow_log(wproduct, 'wproduct_create') # 隐藏原半成品 wps = WProduct.objects.filter(ow_wproduct__operation=op) - wps.update(is_hidden=True, child=wproduct, + wps.update(act_state=WProduct.WPR_ACT_STATE_USED, child=wproduct, is_hidden=True, update_by=request.user, update_time=timezone.now()) + WpmService.add_wproducts_flow_log(wps, change_str='wproduct_create') else: raise exceptions.APIException('产出物料未填写或填写错误') op.is_submited = True