From bd179c20b1403820928b6281d373789ef0082f4f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 19 Nov 2021 10:40:09 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=A6=E9=97=B4=E6=93=8D=E4=BD=9C=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E6=A2=B3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/mtm/serializers.py | 2 +- .../0014_subproductionplan_number.py | 18 ++++++ hb_server/apps/pm/models.py | 1 + hb_server/apps/pm/serializers.py | 5 ++ hb_server/apps/pm/views.py | 6 +- .../qm/migrations/0011_auto_20211119_0848.py | 35 +++++++++++ .../wpm/migrations/0016_auto_20211119_0848.py | 28 +++++++++ .../wpm/migrations/0017_auto_20211119_1034.py | 49 +++++++++++++++ hb_server/apps/wpm/models.py | 15 +++-- hb_server/apps/wpm/serializers.py | 15 ++--- hb_server/apps/wpm/urls.py | 3 +- hb_server/apps/wpm/views.py | 62 ++++++++++++++++--- hb_server/utils/tools.py | 2 +- 13 files changed, 216 insertions(+), 25 deletions(-) create mode 100644 hb_server/apps/pm/migrations/0014_subproductionplan_number.py create mode 100644 hb_server/apps/qm/migrations/0011_auto_20211119_0848.py create mode 100644 hb_server/apps/wpm/migrations/0016_auto_20211119_0848.py create mode 100644 hb_server/apps/wpm/migrations/0017_auto_20211119_1034.py diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index f9cc55d..f1b3ab8 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -26,7 +26,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer): class MaterialSimpleSerializer(serializers.ModelSerializer): class Meta: model = Material - fields = ['id', 'name', 'number', 'unit','specification'] + fields = ['id', 'name', 'number', 'unit','specification', 'type'] class ProcessSerializer(serializers.ModelSerializer): instruction_ = FileSimpleSerializer(source='instruction', read_only=True) diff --git a/hb_server/apps/pm/migrations/0014_subproductionplan_number.py b/hb_server/apps/pm/migrations/0014_subproductionplan_number.py new file mode 100644 index 0000000..e84a4d8 --- /dev/null +++ b/hb_server/apps/pm/migrations/0014_subproductionplan_number.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2021-11-19 02:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0013_alter_subproductionplan_subproduction'), + ] + + operations = [ + migrations.AddField( + model_name='subproductionplan', + name='number', + field=models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='子计划编号'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 663d885..fa4711f 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -44,6 +44,7 @@ class SubProductionPlan(CommonAModel): (SUBPLAN_STATE_WORKING, '生产中'), (SUBPLAN_STATE_DONE, '已完成') ) + number = models.CharField('子计划编号', max_length=50, unique=True, null=True, blank=True) production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE, related_name='subplan_subprod') start_date = models.DateField('计划开工日期') diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 90ff400..65980c0 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -55,3 +55,8 @@ class PickNeedSerializer(serializers.Serializer): class PlanDestorySerializer(serializers.Serializer): ids = serializers.ListField(child=serializers.IntegerField(), label='主计划ID列表') + +class SubproductionPlanSimpleSerializer(serializers.ModelSerializer): + class Meta: + model = SubProductionPlan + fields = ['id', 'number'] diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index a86d146..56446bf 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -62,7 +62,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel pass else: raise APIException('排产数量错误') - instance = serializer.save(create_by=request.user, product=order.product, number='JH-'+ranstr(7)) + instance = serializer.save(create_by=request.user, product=order.product, number='JH'+ranstr(7)) updateOrderPlanedCount(instance.order) return Response() @@ -83,13 +83,13 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel if production_plan.is_planed: raise APIException('已生成子计划') subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number') - for i in subps: + for index, i in enumerate(subps): steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False, usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark') instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i, start_date=production_plan.start_date, end_date=production_plan.end_date, workshop=i.process.workshop, process=i.process, create_by=request.user, - steps = list(steps)) + steps = list(steps), number=production_plan.number + '-' + str(index+1)) # 生成子计划物料需求/进度 for m in SubprodctionMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): spro = SubProductionProgress.objects.create(material=m.material, type=m.type, diff --git a/hb_server/apps/qm/migrations/0011_auto_20211119_0848.py b/hb_server/apps/qm/migrations/0011_auto_20211119_0848.py new file mode 100644 index 0000000..a643fe0 --- /dev/null +++ b/hb_server/apps/qm/migrations/0011_auto_20211119_0848.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.9 on 2021-11-19 00:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0013_alter_subproductionplan_subproduction'), + ('qm', '0010_rename_m_state_testrecord_material'), + ] + + operations = [ + migrations.AddField( + model_name='testrecord', + name='is_testok_robot', + field=models.BooleanField(default=True, verbose_name='自动判定的是否合格'), + ), + migrations.AddField( + model_name='testrecord', + name='number', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='产品编号'), + ), + migrations.AddField( + model_name='testrecord', + name='subproduction_plan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联的生产子计划'), + ), + migrations.AddField( + model_name='testrecorditem', + name='is_testok_robot', + field=models.BooleanField(blank=True, null=True, verbose_name='自动判定的是否合格'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0016_auto_20211119_0848.py b/hb_server/apps/wpm/migrations/0016_auto_20211119_0848.py new file mode 100644 index 0000000..e632e92 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0016_auto_20211119_0848.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.9 on 2021-11-19 00:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0015_auto_20211117_2332'), + ] + + operations = [ + migrations.RemoveField( + model_name='operation', + name='wproducts', + ), + migrations.AddField( + model_name='operation', + name='is_submited', + field=models.BooleanField(default=True, verbose_name='是否提交'), + ), + migrations.AddField( + model_name='wproduct', + name='operation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_operation', to='wpm.operation', verbose_name='关联操作'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0017_auto_20211119_1034.py b/hb_server/apps/wpm/migrations/0017_auto_20211119_1034.py new file mode 100644 index 0000000..bbbaeaa --- /dev/null +++ b/hb_server/apps/wpm/migrations/0017_auto_20211119_1034.py @@ -0,0 +1,49 @@ +# Generated by Django 3.2.9 on 2021-11-19 02:34 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('em', '0009_auto_20210916_1108'), + ('wpm', '0016_auto_20211119_0848'), + ] + + operations = [ + migrations.RemoveField( + model_name='operationrecord', + name='create_by', + ), + migrations.RemoveField( + model_name='operationrecord', + name='update_by', + ), + migrations.AddField( + model_name='operationrecord', + name='is_filled', + field=models.BooleanField(default=True, verbose_name='是否填写'), + ), + migrations.AlterField( + model_name='operation', + name='is_submited', + field=models.BooleanField(default=False, verbose_name='是否提交'), + ), + migrations.CreateModel( + name='OperationEquip', + 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='删除标记')), + ('remark', models.TextField(blank=True, null=True, verbose_name='备注')), + ('equip', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='生产设备')), + ('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联操作')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 6e2f647..a00724e 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -9,6 +9,7 @@ from utils.model import SoftModel, BaseModel from simple_history.models import HistoricalRecords from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm from django.core.validators import MinValueValidator +from apps.em.models import Equipment class WMaterial(BaseModel): """ 车间生产物料 @@ -44,17 +45,17 @@ class WProduct(CommonAModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE) production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE) warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True) - # operation = models.ForeignKey('wpm.operation', verbose_name='关联操作', - # on_delete=models.SET_NULL, null=True, blank=True, related_name='current_operation') + operation = models.ForeignKey('wpm.operation', verbose_name='关联操作', + on_delete=models.SET_NULL, null=True, blank=True, related_name='current_operation') class Operation(CommonADModel): """ 生产操作 """ - wproducts = models.ManyToManyField(WProduct, verbose_name='关联半成品', through='wpm.operationwproduct') step = models.ForeignKey(Step, verbose_name='操作步骤', on_delete=models.CASCADE, null=True, blank=True) use_scrap = models.BooleanField('是否使用的边角料', default=False) remark = models.CharField('操作备注', max_length=200, null=True, blank=True) + is_submited = models.BooleanField('是否提交', default=False) class OperationWproduct(BaseModel): """ @@ -82,13 +83,13 @@ class OperationMaterial(BaseModel): material = models.ForeignKey(Material, verbose_name='可能产出的副产品', on_delete=models.CASCADE, null=True, blank=True) count = models.IntegerField('消耗或产出数量', validators=[MinValueValidator(0)]) -class OperationRecord(CommonAModel): +class OperationRecord(BaseModel): """ 记录表格 """ form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE) operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE) - + is_filled = models.BooleanField('是否填写', default=True) class OperationRecordItem(BaseModel): """ @@ -102,3 +103,7 @@ class OperationRecordItem(BaseModel): sort = models.IntegerField('排序号', default=1) operation_record = models.ForeignKey(OperationRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE) +class OperationEquip(BaseModel): + operation = models.ForeignKey(Operation, verbose_name='关联操作', on_delete=models.CASCADE) + equip = models.ForeignKey(Equipment, verbose_name='生产设备', on_delete=models.CASCADE) + remark = models.TextField('备注', null=True, blank=True) \ No newline at end of file diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index aad7ef7..a79a118 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,9 +8,10 @@ from apps.mtm.serializers import MaterialSimpleSerializer, StepSimpleSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from apps.pm.serializers import SubproductionPlanSimpleSerializer from apps.qm.models import TestRecord, TestRecordItem from apps.system.serializers import UserSimpleSerializer -from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem +from apps.wpm.models import Operation, OperationWproduct, WMaterial, WProduct, OperationRecord, OperationRecordItem from django.db import transaction class PickHalfSerializer(serializers.Serializer): @@ -130,20 +131,14 @@ class WProductListSerializer(serializers.ModelSerializer): fields = '__all__' class OperationDetailSerializer(serializers.ModelSerializer): - wproducts_ = serializers.SerializerMethodField() create_by_ = UserSimpleSerializer(source='create_by', read_only=True) - material_ = MaterialSimpleSerializer(source='material', read_only=True) step_ = StepSimpleSerializer(source='step', read_only=True) class Meta: model = Operation fields = '__all__' - - def get_wproducts_(self, obj): - return list(WProduct.objects.filter(id__in=obj.wproducts).values('id', 'number')) class OperationListSerializer(serializers.ModelSerializer): create_by_ = UserSimpleSerializer(source='create_by', read_only=True) - material_ = MaterialSimpleSerializer(source='material', read_only=True) step_ = StepSimpleSerializer(source='step', read_only=True) class Meta: model = Operation @@ -231,6 +226,12 @@ class OperationRecordSerializer(serializers.ModelSerializer): model = OperationRecord fields = ['form', 'record_data'] +class OperationWproductListSerializer(serializers.ModelSerializer): + material_ = MaterialSimpleSerializer(source='material', read_only=True) + subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) + class Meta: + model = OperationWproduct + fields = '__all__' class OperationSubmitSerializer(serializers.Serializer): diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index 7c43ac7..b5d4018 100644 --- a/hb_server/apps/wpm/urls.py +++ b/hb_server/apps/wpm/urls.py @@ -3,12 +3,13 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.wpm.views import DoFormInit, DoFormSubmit, OperationViewSet, WMaterialViewSet, WPlanViewSet, WProductViewSet +from apps.wpm.views import DoFormInit, DoFormSubmit, OperationViewSet, OperationWproductViewSet, WMaterialViewSet, WPlanViewSet, WProductViewSet router = DefaultRouter() router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('wproduct', WProductViewSet, basename='wproduct') router.register('operation', OperationViewSet, basename='operation') +router.register('operation_wproduct', OperationWproductViewSet, basename='operation_wproduct') router.register('subplan', WPlanViewSet, basename='wplan') urlpatterns = [ path('do/init/', DoFormInit.as_view()), diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 6244191..913712a 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -15,9 +15,9 @@ from apps.qm.models import TestRecordItem from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action -from apps.wpm.models import OperationWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem +from apps.wpm.models import OperationEquip, OperationWproduct, WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem -from apps.wpm.serializers import OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer +from apps.wpm.serializers import OperationWproductListSerializer, OperationCreateSerializer, OperationDetailSerializer, OperationListSerializer, PickHalfSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WplanPutInSerializer, WpmTestRecordCreateSerializer, WproductPutInSerializer from rest_framework.response import Response from django.db import transaction from rest_framework import exceptions, serializers @@ -67,7 +67,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet): wps = WProduct.objects.filter(pk__in=[x for x in i['wproducts']]) wps.update(step=first_step, is_executed=False, act_state=WProduct.WPR_ACT_STATE_DOING, is_hidden=False, warehouse=None, - subproduction_plan=sp, production_plan=sp.production_plan) + subproduction_plan=sp, production_plan=sp.production_plan, update_by=request.user, update_time=timezone.now()) return Response() @@ -125,7 +125,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet): IProduct.objects.bulk_create(ips2) # 更新库存并修改半成品进行状态 update_inm(fifo) - wproducts.update(act_sate=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse) + wproducts.update(act_sate=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now()) return Response() @@ -156,7 +156,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 半成品 """ perms_map={'*':'*'} - queryset = WProduct.objects.select_related('step', 'material').filter(is_hidden=False) + queryset = WProduct.objects.select_related('step', 'material').filter(is_hidden=False).exclude(operation=None) serializer_class = WProductListSerializer filterset_fields = ['step', 'subproduction_plan', 'material', 'production_plan', 'step__process', 'act_state'] search_fields = ['number'] @@ -199,7 +199,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): if obj.is_testok: wproduct.act_state = WProduct.WPR_ACT_STATE_OK if wproduct.number is None: # 产生半成品编号 - wproduct.number = 'WP-'+ranstr(7) + wproduct.number = 'WP'+ranstr(7) wproduct.save() # 更新子计划状态 # 更新子计划主产品数 @@ -266,7 +266,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Gen perms_map={'*':'*'} queryset = Operation.objects.select_related('step', 'material').all() serializer_class = OperationListSerializer - filterset_fields = ['step', 'step__process'] + filterset_fields = ['step', 'step__process', 'is_submited'] ordering_fields = ['id'] ordering = ['-id'] @@ -283,8 +283,56 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Gen serializer = OperationCreateSerializer(data=data, context={'request':self.request}) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data #校验之后的数据 + step = Step.objects.get(pk=vdata['step']) + op = Operation() + op.step = step + op.is_submited = False + op.save() + # 创建操作所用半成品关联记录 + if 'wproducts' in vdata: + owps = [] + for i in vdata['wproducts']: + owp = {} + wpd = WProduct.objects.get(pk=i) + owp['operation'] = op + owp['wproduct'] = wpd + owp['number'] = wpd.number + owp['material'] = wpd.material + owp['subproduction_plan'] = wpd.subproduction_plan + owp['production_plan'] = wpd.production_plan + owps.append(OperationWproduct(**owp)) + OperationWproduct.objects.bulk_create(owps) + # 查询需要填写的自定义表格 + forms = RecordForm.objects.filter(step=step, type=RecordForm.RF_TYPE_DO) + for i in forms: + opr = OperationRecord() + opr.operation = op + opr.form = i + opr.is_filled = False + opr.save() + # 查询需要使用的生产设备 + equips = step.equipements + for i in equips: + ope = OperationEquip() + ope.operation = op + ope.equip = i + ope.save() return Response() + + + +class OperationWproductViewSet(ListModelMixin, GenericViewSet): + """ + 操作使用的半成品 + """ + perms_map={'*':'*'} + queryset = OperationWproduct.objects.select_related('subproduction_plan', 'material').all() + serializer_class = OperationWproductListSerializer + filterset_fields = ['material', 'subproduction_plan', 'operation'] + ordering_fields = ['id'] + ordering = ['-id'] + class DoFormInit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} serializer_class=OperationInitSerializer diff --git a/hb_server/utils/tools.py b/hb_server/utils/tools.py index 786b8f2..73a6db6 100644 --- a/hb_server/utils/tools.py +++ b/hb_server/utils/tools.py @@ -2,6 +2,6 @@ import random import string def ranstr(num): - salt = ''.join(random.sample(string.ascii_letters + string.digits, num)) + salt = ''.join(random.sample(string.ascii_lowercase + string.digits, num)) return salt ranstr(10) \ No newline at end of file