From 2b3f45eb7f847a8647a4e39b478d722ed9030a46 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 00:14:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=A1=A8=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_client/src/views/monitor/service.vue | 2 +- hb_server/apps/inm/models.py | 12 +- hb_server/apps/inm/serializers.py | 2 +- hb_server/apps/inm/signals.py | 4 +- .../apps/mtm/migrations/0029_step_type.py | 18 +++ hb_server/apps/mtm/models.py | 42 ++++-- hb_server/apps/mtm/serializers.py | 10 +- hb_server/apps/mtm/views.py | 6 +- .../0012_alter_subproductionprogress_type.py | 18 +++ hb_server/apps/pm/models.py | 9 +- hb_server/apps/pm/views.py | 4 +- hb_server/apps/wpm/migrations/0001_initial.py | 1 - .../wpm/migrations/0005_auto_20211108_0901.py | 5 - .../wpm/migrations/0008_wproduct_is_hidden.py | 18 +++ .../wpm/migrations/0009_wproduct_parent.py | 18 +++ .../wpm/migrations/0010_auto_20211108_2335.py | 136 ++++++++++++++++++ hb_server/apps/wpm/models.py | 19 ++- hb_server/apps/wpm/serializers.py | 31 ++-- hb_server/apps/wpm/views.py | 97 +++++++++---- 19 files changed, 354 insertions(+), 98 deletions(-) create mode 100644 hb_server/apps/mtm/migrations/0029_step_type.py create mode 100644 hb_server/apps/pm/migrations/0012_alter_subproductionprogress_type.py create mode 100644 hb_server/apps/wpm/migrations/0008_wproduct_is_hidden.py create mode 100644 hb_server/apps/wpm/migrations/0009_wproduct_parent.py create mode 100644 hb_server/apps/wpm/migrations/0010_auto_20211108_2335.py diff --git a/hb_client/src/views/monitor/service.vue b/hb_client/src/views/monitor/service.vue index 8c9fcdd..b9473ff 100644 --- a/hb_client/src/views/monitor/service.vue +++ b/hb_client/src/views/monitor/service.vue @@ -66,7 +66,7 @@ - +
日志列表
diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py index fc21596..8446014 100644 --- a/hb_server/apps/inm/models.py +++ b/hb_server/apps/inm/models.py @@ -53,11 +53,15 @@ class FIFO(CommonAModel): """ 出入库记录 """ + FIFO_TYPE_DO_OUT = 1 # 生产领料 + FIFO_TYPE_SALE_OUT = 2 + FIFO_TYPE_PUR_IN = 3 + FIFO_TYPE_DO_IN = 4 type_choices = ( - (1, '生产领料'), - (2, '销售提货'), - (3, '采购入库'), - (4, '生产入库') + (FIFO_TYPE_DO_OUT, '生产领料'), + (FIFO_TYPE_SALE_OUT, '销售提货'), + (FIFO_TYPE_PUR_IN, '采购入库'), + (FIFO_TYPE_DO_IN, '生产入库') ) type = models.IntegerField('出入库类型', default=1) is_audited = models.BooleanField('是否审核', default=False) diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 62461c4..3bafb90 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -91,7 +91,7 @@ class FIFOInPurSerializer(serializers.ModelSerializer): pass # 创建采购入库 - validated_data['type'] = 3 + validated_data['type'] = FIFO.FIFO_TYPE_PUR_IN obj = FIFO(**validated_data) obj.save() for i in details: diff --git a/hb_server/apps/inm/signals.py b/hb_server/apps/inm/signals.py index f3fd13f..57235a6 100644 --- a/hb_server/apps/inm/signals.py +++ b/hb_server/apps/inm/signals.py @@ -9,7 +9,7 @@ def update_inm(instance:FIFO, type:int=1): 更新库存(正反) """ warehouse = instance.warehouse - if instance.type in [3]: # 采购入库 + if instance.type in [FIFO.FIFO_TYPE_PUR_IN]: # 采购入库 # 更新相关表 for i in FIFOItem.objects.filter(fifo=instance): material = i.material @@ -23,7 +23,7 @@ def update_inm(instance:FIFO, type:int=1): o2.save() material.count = material.count + i.count material.save() - elif instance.type in [1]: # 生产领料 + elif instance.type in [FIFO.FIFO_TYPE_DO_OUT]: # 生产领料 # 更新相关表 for i in FIFOItem.objects.filter(fifo=instance): material = i.material diff --git a/hb_server/apps/mtm/migrations/0029_step_type.py b/hb_server/apps/mtm/migrations/0029_step_type.py new file mode 100644 index 0000000..d370806 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0029_step_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 09:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ] + + operations = [ + migrations.AddField( + model_name='step', + name='type', + field=models.IntegerField(choices=[(1, '普通'), (2, '分割'), (3, '结合')], default=1, verbose_name='操作类型'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index ce615ac..99a19e3 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -12,13 +12,19 @@ class Material(CommonAModel): """ 物料 """ + MA_TYPE_GOOD = 1 + MA_TYPE_HALFGOOD = 2 + MA_TYPE_MAINSO = 3 + MA_TYPE_HELPSO = 4 + MA_TYPE_TOOL = 5 + MA_TYPE_HELPTOOL = 6 type_choices=( - (1, '成品'), - (2, '半成品'), - (3, '主要原料'), - (4, '辅助材料') , - (5, '加工工具'), - (6, '辅助工装') + (MA_TYPE_GOOD, '成品'), + (MA_TYPE_HALFGOOD, '半成品'), + (MA_TYPE_MAINSO, '主要原料'), + (MA_TYPE_HELPSO, '辅助材料') , + (MA_TYPE_TOOL, '加工工具'), + (MA_TYPE_HELPTOOL, '辅助工装') ) unit_choices =( ('块', '块'), @@ -62,6 +68,15 @@ class Step(CommonAModel): """ 工序步骤 """ + STEP_TYPE_NOM = 1 + STEP_TYPE_DIV = 2 + STEP_TYPE_COMB = 3 + step_type_choices=( + (STEP_TYPE_NOM, '普通'), + (STEP_TYPE_DIV, '分割'), + (STEP_TYPE_COMB, '结合') + ) + type = models.IntegerField('操作类型', choices=step_type_choices, default=1) process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序', related_name='step_process') name = models.CharField('工序步骤名称', max_length=100) number = models.CharField('步骤编号', max_length=100, null=True, blank=True) @@ -80,9 +95,11 @@ class RecordForm(CommonAModel): """ 记录表格 """ + RF_TYPE_DO = 1 + RF_TYPE_TEST = 2 type_choices=( - (1, '生产记录'), - (2, '检验记录') + (RF_TYPE_DO, '生产记录'), + (RF_TYPE_TEST, '检验记录') ) name = models.CharField('表格名称', max_length=100, unique=True) type = models.IntegerField('表格类型', choices=type_choices, default=1) @@ -164,10 +181,13 @@ class SubprodctionMaterial(CommonAModel): """ 输入/输出物料/工具工装 """ + SUB_MA_TYPE_IN = 1 + SUB_MA_TYPE_OUT = 2 + SUB_MA_TYPE_TOOL = 3 type_choices=( - (1, '输入物料'), - (2, '输出物料'), - (3, '工具工装') + (SUB_MA_TYPE_IN, '输入物料'), + (SUB_MA_TYPE_OUT, '输出物料'), + (SUB_MA_TYPE_TOOL, '工具工装') ) material = models.ForeignKey(Material, verbose_name='物料', on_delete=models.CASCADE, related_name='subplan_material') is_main = models.BooleanField('是否主产出', default=False) # 以该产品完成度计算进度 diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index d9e8411..0d9b82f 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -95,9 +95,9 @@ class InputMaterialSerializer(serializers.ModelSerializer): fields = ['count', 'sort', 'material', 'subproduction'] def create(self, validated_data): - if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=1).exists(): + if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=SubprodctionMaterial.SUB_MA_TYPE_IN).exists(): raise ValidationError('该物料已存在') - validated_data['type']=1 + validated_data['type']=SubprodctionMaterial.SUB_MA_TYPE_IN return super().create(validated_data) class InputMaterialUpdateSerializer(serializers.ModelSerializer): @@ -111,11 +111,11 @@ class OutputMaterialSerializer(serializers.ModelSerializer): fields = ['count', 'sort', 'material', 'subproduction', 'is_main'] def create(self, validated_data): - if SubprodctionMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True, type=2).exists(): + if SubprodctionMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exists(): raise ValidationError('主产出只能有1个') - if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=2).exists(): + if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exists(): raise ValidationError('该物料已存在') - validated_data['type']=2 + validated_data['type']=SubprodctionMaterial.SUB_MA_TYPE_OUT return super().create(validated_data) class OutputMaterialUpdateSerializer(serializers.ModelSerializer): diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 0d7f90b..679f7d2 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -85,7 +85,7 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): 输入物料-增删改查 """ perms_map = {'*':'*'} - queryset = SubprodctionMaterial.objects.select_related('material').filter(type=1) + queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer_class = InputMaterialSerializer filterset_fields = ['subproduction'] ordering = ['sort', '-create_time'] @@ -102,7 +102,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): 输出物料-增删改查 """ perms_map = {'*':'*'} - queryset = SubprodctionMaterial.objects.select_related('material').filter(type=2) + queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_OUT) serializer_class = OutputMaterialSerializer filterset_fields = ['subproduction'] ordering = ['sort', '-create_time'] @@ -119,7 +119,7 @@ class OtherMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, DestroyModel 其他物料-增删改查 """ perms_map = {'*':'*'} - queryset = SubprodctionMaterial.objects.select_related('material').filter(type=3) + queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL) serializer_class = OutputMaterialSerializer filterset_fields = ['subproduction'] ordering = ['sort', '-create_time'] diff --git a/hb_server/apps/pm/migrations/0012_alter_subproductionprogress_type.py b/hb_server/apps/pm/migrations/0012_alter_subproductionprogress_type.py new file mode 100644 index 0000000..cddd556 --- /dev/null +++ b/hb_server/apps/pm/migrations/0012_alter_subproductionprogress_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 09:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pm', '0011_auto_20211104_1006'), + ] + + operations = [ + migrations.AlterField( + model_name='subproductionprogress', + name='type', + field=models.IntegerField(default=((1, '输入物料'), (2, '输出物料'), (3, '工具工装')), verbose_name='物料应用类型'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 62b02e8..bc59ec9 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -7,7 +7,7 @@ import django.utils.timezone as timezone from django.db.models.query import QuerySet from utils.model import SoftModel, BaseModel -from apps.mtm.models import Material, Process, SubProduction +from apps.mtm.models import Material, Process, SubProduction, SubprodctionMaterial from apps.sam.models import Order class ProductionPlan(CommonAModel): @@ -65,15 +65,10 @@ class SubProductionProgress(BaseModel): """ 子计划生产进度统计表/物料消耗 """ - type_choices=( - (1, '输入物料'), - (2, '输出物料'), - (3, '工具工装') - ) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan') material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) is_main = models.BooleanField('是否主产出', default=False) - type = models.IntegerField('物料应用类型', default=1) + type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices) count = models.IntegerField('应出入数') count_pick = models.IntegerField('实际领用数', default=0) count_real = models.IntegerField('实际消耗/产出数', default=0) diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index c646ab6..682e59d 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -155,7 +155,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo 领料需求清单 """ obj = self.get_object() - instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1) + instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer = SubProductionProgressSerializer(instance=instance, many=True) return Response(serializer.data) @@ -165,7 +165,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo 领料需求清单/库存数 """ obj = self.get_object() - instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1) + instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer = SubProductionProgressSerializer(instance=instance, many=True) need = serializer.data materials = [] diff --git a/hb_server/apps/wpm/migrations/0001_initial.py b/hb_server/apps/wpm/migrations/0001_initial.py index 75511e7..6a6fb00 100644 --- a/hb_server/apps/wpm/migrations/0001_initial.py +++ b/hb_server/apps/wpm/migrations/0001_initial.py @@ -30,7 +30,6 @@ class Migration(migrations.Migration): ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), ('m_state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')), ('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤')), - ('parent', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='上一级')), ('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')), ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), ], diff --git a/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py b/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py index 174fc1d..bc8dd8b 100644 --- a/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py +++ b/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py @@ -95,11 +95,6 @@ class Migration(migrations.Migration): name='act_state', field=models.IntegerField(choices=[(1, '生产中'), (2, '待检测'), (3, '已合格')], default=0, verbose_name='进行状态'), ), - migrations.AlterField( - model_name='wproduct', - name='parent', - field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='上一级'), - ), migrations.AddField( model_name='wproductrecorddetail', name='wproduct_record', diff --git a/hb_server/apps/wpm/migrations/0008_wproduct_is_hidden.py b/hb_server/apps/wpm/migrations/0008_wproduct_is_hidden.py new file mode 100644 index 0000000..36cb2bd --- /dev/null +++ b/hb_server/apps/wpm/migrations/0008_wproduct_is_hidden.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 09:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0007_alter_wproductrecorditem_field_type'), + ] + + operations = [ + migrations.AddField( + model_name='wproduct', + name='is_hidden', + field=models.BooleanField(default=False, verbose_name='是否隐藏'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0009_wproduct_parent.py b/hb_server/apps/wpm/migrations/0009_wproduct_parent.py new file mode 100644 index 0000000..5e7b88a --- /dev/null +++ b/hb_server/apps/wpm/migrations/0009_wproduct_parent.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 15:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0008_wproduct_is_hidden'), + ] + + operations = [ + migrations.AddField( + model_name='wproduct', + name='parent', + field=models.JSONField(blank=True, default=list, verbose_name='父'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0010_auto_20211108_2335.py b/hb_server/apps/wpm/migrations/0010_auto_20211108_2335.py new file mode 100644 index 0000000..4a67281 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0010_auto_20211108_2335.py @@ -0,0 +1,136 @@ +# Generated by Django 3.2.6 on 2021-11-08 15:35 + +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 = [ + ('mtm', '0029_step_type'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('wpm', '0009_wproduct_parent'), + ] + + operations = [ + migrations.CreateModel( + name='Operation', + 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='删除标记')), + ('wproducts', models.JSONField(blank=True, default=list, verbose_name='关联产品ID列表')), + ('remark', 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='operation_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('m_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='操作时的物料状态')), + ('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='操作步骤')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operation_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OperationMaterial', + 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.IntegerField(choices=[(1, '消耗'), (2, '产出')], default=0, verbose_name='类型')), + ('count', models.IntegerField(verbose_name='消耗或产出数量')), + ('material', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='可能产出的副产品')), + ('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产操作')), + ('wmaterial', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wmaterial', verbose_name='关联的车间物料')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OperationRecord', + 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='删除标记')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operationrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用的生产记录表格')), + ('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产操作')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operationrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OperationRecordItem', + 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='删除标记')), + ('field_name', models.CharField(max_length=50, verbose_name='字段名')), + ('field_key', models.CharField(max_length=50, verbose_name='字段标识')), + ('field_type', models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型')), + ('field_value', models.JSONField(blank=True, default=dict, verbose_name='录入值')), + ('sort', models.IntegerField(default=1, verbose_name='排序号')), + ('form_field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联字段')), + ('operation_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产记录')), + ], + options={ + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='wproductmaterial', + name='material', + ), + migrations.RemoveField( + model_name='wproductmaterial', + name='wmaterial', + ), + migrations.RemoveField( + model_name='wproductmaterial', + name='wproduct_action', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='create_by', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='form', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='update_by', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='wproduct_action', + ), + migrations.RemoveField( + model_name='wproductrecorditem', + name='form_field', + ), + migrations.RemoveField( + model_name='wproductrecorditem', + name='wproduct_record', + ), + migrations.DeleteModel( + name='WProductAction', + ), + migrations.DeleteModel( + name='WProductMaterial', + ), + migrations.DeleteModel( + name='WProductRecord', + ), + migrations.DeleteModel( + name='WProductRecordItem', + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index d1c80d8..0aa63cc 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -12,8 +12,6 @@ class WMaterial(BaseModel): """ 车间生产物料 """ - # workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) - # process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子计划', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) batch = models.CharField('批次号', max_length=100, null=True, blank=True) @@ -36,13 +34,14 @@ class WProduct(CommonAModel): p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True) act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices) is_executed = models.BooleanField('子工序是否已执行', default=False) - parent = models.ForeignKey('self', verbose_name='上一级', on_delete=models.CASCADE, db_constraint=False, null=True, blank=True) + is_hidden = models.BooleanField('是否隐藏', default=False) + parent = models.JSONField('父', default=list, blank=True) remark = models.CharField('备注', max_length=200, null=True, blank=True) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE) production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE) -class WProductAction(CommonAModel): +class Operation(CommonAModel): """ 生产操作 """ @@ -51,7 +50,7 @@ class WProductAction(CommonAModel): p_state = models.ForeignKey(Step, verbose_name='操作步骤', on_delete=models.CASCADE, null=True, blank=True) remark = models.CharField('操作备注', max_length=200, null=True, blank=True) -class WProductMaterial(BaseModel): +class OperationMaterial(BaseModel): """ 车间生产物料消耗产出表 """ @@ -60,20 +59,20 @@ class WProductMaterial(BaseModel): (2, '产出') ) type = models.IntegerField('类型', default=0, choices=type_choices) - wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) + operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE) wmaterial = models.ForeignKey(WMaterial, verbose_name='关联的车间物料', on_delete=models.CASCADE, null=True, blank=True) material = models.ForeignKey(Material, verbose_name='可能产出的副产品', on_delete=models.CASCADE, null=True, blank=True) count = models.IntegerField('消耗或产出数量') -class WProductRecord(CommonAModel): +class OperationRecord(CommonAModel): """ 记录表格 """ form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE) - wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) + operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE) -class WProductRecordItem(BaseModel): +class OperationRecordItem(BaseModel): """ 记录表格字段值 """ @@ -83,5 +82,5 @@ class WProductRecordItem(BaseModel): field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50) field_value = models.JSONField('录入值', default=dict, blank=True) sort = models.IntegerField('排序号', default=1) - wproduct_record = models.ForeignKey(WProductRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE) + operation_record = models.ForeignKey(Operation, verbose_name='关联的生产记录', on_delete=models.CASCADE) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 5cad10a..3609a70 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -7,8 +7,8 @@ from apps.mtm.serializers import MaterialSimpleSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from django.utils import timezone - -from apps.wpm.models import WMaterial, WProduct, WProductRecord, WProductRecordItem +from django.utils.translation import gettext_lazy as _ +from apps.wpm.models import WMaterial, WProduct, OperationRecord, OperationRecordItem class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -38,7 +38,7 @@ class PickSerializer(serializers.Serializer): operator = self.context['request'].user validated_data['create_by'] = operator validated_data['operator'] = operator - validated_data['type'] = 1 + validated_data['type'] = FIFO.FIFO_TYPE_DO_OUT validated_data['inout_date'] = timezone.now() fifo = FIFO.objects.create(**validated_data) for i in picks: @@ -81,7 +81,7 @@ class WMaterialListSerializer(serializers.ModelSerializer): fields = '__all__' -class WActionInitSerializer(serializers.Serializer): +class OperationInitSerializer(serializers.Serializer): step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") wproducts = serializers.ListField(child= @@ -94,17 +94,16 @@ class WActionInitSerializer(serializers.Serializer): stepIds=[i['id'] for i in subproduction_plan.steps] if step.id not in stepIds: raise serializers.ValidationError('请选择正确的子工序操作') - - if 'wproducts' in data: + + if 'wproducts' in data and data['wproducts']: for i in data['wproducts']: if i.subproduction_plan != subproduction_plan: raise serializers.ValidationError('半成品所属子计划不一致') if i.step != step: raise serializers.ValidationError('半成品所属子工序不一致') else: - if WProduct.objects.filter(subproduction_plan__production_plan=subproduction_plan.production_plan, - is_deleted=False).exists(): # 存在动态半成品 # 这里后续需要更改比如报废状态 - raise serializers.ValidationError('请选择半成品进行操作') + if step.type != Step.STEP_TYPE_DIV: + raise serializers.ValidationError(_('请选择半成品进行操作')) return data @@ -116,27 +115,27 @@ class DoOutputSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label='物料ID') count_output = serializers.IntegerField(min_value=0, label='产出数量') -class WProductRecordItemSerializer(serializers.ModelSerializer): +class OperationRecordItemSerializer(serializers.ModelSerializer): class Meta: - model = WProductRecordItem + model = OperationRecordItem fields = ['form_field', 'field_value'] -class WProductRecordSerializer(serializers.ModelSerializer): - record_data = WProductRecordItemSerializer(many=True) +class OperationRecordSerializer(serializers.ModelSerializer): + record_data = OperationRecordItemSerializer(many=True) class Meta: - model = WProductRecord + model = OperationRecord fields = ['form', 'record_data'] -class WActionSubmitSerializer(serializers.Serializer): +class OperationSubmitSerializer(serializers.Serializer): step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") wproducts = serializers.ListField(child= serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False) input = DoInputSerializer(many=True, required=False) output = DoOutputSerializer(many=True, required=False) - forms = WProductRecordSerializer(many=True, required=False) + forms = OperationRecordSerializer(many=True, required=False) \ No newline at end of file diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index f5a2dd0..270dc11 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -5,16 +5,16 @@ from rest_framework.utils import serializer_helpers from rest_framework.utils.field_mapping import get_relation_kwargs from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ModelViewSet -from apps.mtm.models import Material, RecordForm, Step +from apps.mtm.models import Material, RecordForm, Step, SubprodctionMaterial from apps.mtm.serializers import RecordFormDetailSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action -from apps.wpm.models import WMaterial, WProduct, WProductAction, WProductMaterial, WProductRecord, WProductRecordItem +from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem -from apps.wpm.serializers import PickSerializer, WActionInitSerializer, WActionSubmitSerializer, WMaterialListSerializer +from apps.wpm.serializers import PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer from rest_framework.response import Response # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): @@ -52,13 +52,13 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): class DoFormInit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} - serializer_class=WActionInitSerializer + serializer_class=OperationInitSerializer def post(self, request, format=None): """ 调用操作表单 """ data = request.data - serializer = WActionInitSerializer(data=data) + serializer = OperationInitSerializer(data=data) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data ret = {} @@ -75,15 +75,14 @@ class DoFormInit(CreateAPIView, GenericAPIView): for i in ret_0['input']: i['count_input'] = 0 # 需要输出的物料 - # 如果传入半成品列表就不需要 if ret_0['wproducts']: - # 排除wproduct列表 - # mids = WProduct.objects.filter(pk__in=data['wproducts']).values_list('m_state', flat=True) + # 排除主要产物, 因为已经放到半成品里了, 由半成品进行处理, 夹层可能需要特殊处理 o_objs = SubProductionProgress.objects.filter( - subproduction_plan=vdata['subproduction_plan'], type=2).exclude(is_main=True) + subproduction_plan=vdata['subproduction_plan'], type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exclude(is_main=True) + else: o_objs = SubProductionProgress.objects.filter( - subproduction_plan=vdata['subproduction_plan'], type=2) + subproduction_plan=vdata['subproduction_plan'], type=SubprodctionMaterial.SUB_MA_TYPE_OUT) ret_0['output'] = list(o_objs.values('material', 'material__name', 'material__number')) for i in ret_0['output']: i['count_output']=0 @@ -91,7 +90,7 @@ class DoFormInit(CreateAPIView, GenericAPIView): ret_0['id'] = 0 ret_0['name'] = '基本信息' ret['forms'].append(ret_0) - forms = RecordForm.objects.filter(step=vdata['step'], type=1) + forms = RecordForm.objects.filter(step=vdata['step'], type=RecordForm.RF_TYPE_DO) if forms.exists(): ret['forms'].extend(RecordFormDetailSerializer(instance=forms, many=True).data) return Response(ret) @@ -99,17 +98,18 @@ class DoFormInit(CreateAPIView, GenericAPIView): class DoFormSubmit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} - serializer_class = WActionSubmitSerializer + serializer_class = OperationSubmitSerializer def post(self, request, format=None): """ 提交操作表单 """ data = request.data - serializer = WActionSubmitSerializer(data=data, context={'request':self.request}) + serializer = OperationSubmitSerializer(data=data, context={'request':self.request}) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data #校验之后的数据 + # 创建一个生产操作记录 - action_obj = WProductAction() + action_obj = Operation() action_obj.p_state = vdata['step'] if 'wproducts' in data and data['wproducts']: action_obj.wproducts = data['wproducts'] @@ -122,7 +122,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): for i in vdata['input']: if i['count_input']: #如果有消耗 i_wmat = i['id'] - WProductMaterial.objects.create(type=1, wproduct_action=action_obj, + OperationMaterial.objects.create(type=1, operation=action_obj, wmaterial= i_wmat, count=i['count_input']) # 更新车间物料 i_wmat.count = i_wmat.count- i['count_input'] @@ -139,15 +139,20 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): ma = i['material'] if ma.is_main: # 计划开始, 第一步切割创建动态产品 - wpr = dict(m_state=ma, p_state=vdata['step'], - act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=True, remark='', - subproduction_plan=vdata['subproduction_plan'], - production_plan=vdata['subproduction_plan'].production_plan) - for x in range(i['count_output']): - WProduct.objects.create(**wpr) + # 如果是切割 + # 获取下一步子工序 + if vdata['step'].type == Step.STEP_TYPE_DIV: + stepIds = [i['id'] for i in vdata['subproduction_plan'].steps] + pindex = stepIds.index(vdata['step'].id) + wpr = dict(m_state=ma, p_state=Step.objects.get(pk=stepIds[pindex+1]), + act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='', + subproduction_plan=vdata['subproduction_plan'], + production_plan=vdata['subproduction_plan'].production_plan) + for x in range(i['count_output']): + WProduct.objects.create(**wpr) else: - # 更新操作消耗物料表 - WProductMaterial.objects.create(type=2, wproduct_action=action_obj, + # 更新操作产出物料表 + OperationMaterial.objects.create(type=2, operation=action_obj, material= ma, count=i['count_output']) # 更新车间物料表 ins, _ = WMaterial.objects.get_or_create(subproduction_plan=vdata['subproduction_plan'], @@ -160,17 +165,49 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): sp.count_real = sp.count_real + i['count_input'] sp.save() - # 更新主产出 + # 更新动态产品表 if 'wproducts' in data and data['wproducts']: wproducts = WProduct.objects.filter(pk__in=data['wproducts']) - wproducts.update(p_state=vdata['step'], is_executed=True) + # 获取下一步子工序 + stepIds = [i['id'] for i in vdata['subproduction_plan'].steps] + pindex = stepIds.index(vdata['step'].id) + if pindex + 1 < len(stepIds): # 如果不是最后一步 + newstep = Step.objects.get(pk=stepIds[pindex+1]) + wproducts.update(p_state=newstep, is_executed=False) + + # 特殊情况如果是夹层结合 + if vdata['step'].type == Step.STEP_TYPE_COMB: + wproducts.update(is_hidden=True) # 隐藏 + + WProduct.objects.create( + m_state=vdata['subproduction_plan'].main_product, p_state = newstep, + act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='', + subproduction_plan=vdata['subproduction_plan'], + production_plan=vdata['subproduction_plan'].production_plan, + parent = data['wproducts'] + ) + + else: # 如果是最后一步, 此时需要转序并更新状态为待检测 + newstep = vdata['step'] + wproducts.update(p_state=newstep, is_executed=True, act_state=WProduct.WPR_ACT_STATE_TOTEST) + + # 特殊情况如果是夹层结合 + if vdata['step'].type == Step.STEP_TYPE_COMB: + wproducts.update(is_hidden=True) # 隐藏 + + WProduct.objects.create( + m_state=vdata['subproduction_plan'].main_product, p_state = newstep, + act_state=WProduct.WPR_ACT_STATE_TOTEST, is_executed=True, remark='', + subproduction_plan=vdata['subproduction_plan'], + production_plan=vdata['subproduction_plan'].production_plan + ) # 保存自定义表单结果 for i in vdata['forms']: - wr = WProductRecord() + wr = OperationRecord() wr.form = i['form'] wr.create_by = request.user - wr.wproduct_action = action_obj + wr.operation = action_obj wr.save() wrds = [] for m in i['record_data']: # 保存记录详情 @@ -180,9 +217,9 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): m['field_type'] = form_field.field_type m['field_value'] = m['field_value'] m['sort'] = form_field.sort - m['wproduct_record'] = wr - wrds.append(WProductRecordItem(**m)) - WProductRecordItem.objects.bulk_create(wrds) + m['operation_record'] = wr + wrds.append(OperationRecordItem(**m)) + Operation.objects.bulk_create(wrds) return Response() \ No newline at end of file