From 29a0a0d33a9b44889c10209ad8c9403e761f31fe Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 4 Nov 2021 09:16:12 +0800 Subject: [PATCH 01/23] =?UTF-8?q?=E8=BD=A6=E9=97=B4=E7=89=A9=E6=96=99?= =?UTF-8?q?=E8=A1=A8=E4=BF=AE=E6=94=B9=E5=AF=B9=E5=BA=94=E5=AD=90=E8=AE=A1?= =?UTF-8?q?=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 4 +- hb_server/apps/mtm/serializers.py | 5 ++ hb_server/apps/pm/apps.py | 2 + .../pm/migrations/0010_auto_20211103_1700.py | 53 ++++++++++++++++ hb_server/apps/pm/models.py | 13 +++- hb_server/apps/pm/serializers.py | 7 ++- hb_server/apps/pm/signals.py | 18 ++++++ hb_server/apps/pm/views.py | 9 ++- .../wpm/migrations/0004_auto_20211104_0914.py | 61 +++++++++++++++++++ hb_server/apps/wpm/models.py | 11 ++-- hb_server/apps/wpm/serializers.py | 19 ++++-- hb_server/apps/wpm/urls.py | 3 +- hb_server/apps/wpm/views.py | 17 +++++- hb_server/utils/model.py | 21 ++++++- 14 files changed, 220 insertions(+), 23 deletions(-) create mode 100644 hb_server/apps/pm/migrations/0010_auto_20211103_1700.py create mode 100644 hb_server/apps/pm/signals.py create mode 100644 hb_server/apps/wpm/migrations/0004_auto_20211104_0914.py diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 0fd15d8..2c3bd49 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -74,7 +74,7 @@ class FIFODetailViewSet(ListModelMixin, GenericViewSet): search_fields = [] ordering_fields = ['create_time'] ordering = ['-create_time'] - + class FIFOViewSet(ListModelMixin, GenericViewSet): """ 出入库记录 @@ -111,6 +111,8 @@ class FIFOViewSet(ListModelMixin, GenericViewSet): for i in FIFODetail.objects.filter(fifo=obj): if not i.is_testok: raise APIException('未检验通过, 不可审核') + if obj.is_audited: + raise APIException('该入库记录已审核通过') obj.is_audited = True obj.save() update_inm(obj) # 更新库存 diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 49f4b87..d9e8411 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -61,6 +61,11 @@ class StepDetailSerializer(serializers.ModelSerializer): queryset = queryset.prefetch_related('equipments') return queryset +class SubProductionSimpleSerializer(serializers.ModelSerializer): + class Meta: + model = SubProduction + fields = ['id', 'name'] + class SubProductionSerializer(serializers.ModelSerializer): process_ = ProcessSimpleSerializer(source='process', read_only=True) class Meta: diff --git a/hb_server/apps/pm/apps.py b/hb_server/apps/pm/apps.py index c0c4b31..7fa3289 100644 --- a/hb_server/apps/pm/apps.py +++ b/hb_server/apps/pm/apps.py @@ -5,3 +5,5 @@ class SamConfig(AppConfig): verbose_name = '生产计划管理' + def ready(self): + import apps.pm.signals \ No newline at end of file diff --git a/hb_server/apps/pm/migrations/0010_auto_20211103_1700.py b/hb_server/apps/pm/migrations/0010_auto_20211103_1700.py new file mode 100644 index 0000000..ba5b40c --- /dev/null +++ b/hb_server/apps/pm/migrations/0010_auto_20211103_1700.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.6 on 2021-11-03 09:00 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ('pm', '0009_auto_20211029_1017'), + ] + + operations = [ + migrations.AddField( + model_name='subproductionplan', + name='main_count', + field=models.IntegerField(default=1, verbose_name='应产出数'), + preserve_default=False, + ), + migrations.AddField( + model_name='subproductionplan', + name='main_count_real', + field=models.IntegerField(default=0, verbose_name='实际产出数'), + ), + migrations.AddField( + model_name='subproductionplan', + name='main_product', + field=models.ForeignKey(default=19, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='主要产品'), + preserve_default=False, + ), + migrations.AddField( + model_name='subproductionprogress', + name='count_pick', + field=models.IntegerField(default=0, verbose_name='实际领用数'), + ), + migrations.AddField( + model_name='subproductionprogress', + name='is_main', + field=models.BooleanField(default=False, verbose_name='是否主产出'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count', + field=models.IntegerField(verbose_name='应出入数'), + ), + migrations.AlterField( + model_name='subproductionprogress', + name='count_real', + field=models.IntegerField(default=0, verbose_name='实际消耗/产出数'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index 2a00e1a..f528c64 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -43,9 +43,16 @@ class SubProductionPlan(CommonAModel): subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) start_date = models.DateField('计划开工日期') end_date = models.DateField('计划完工日期') + workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) + + main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE) + main_count = models.IntegerField('应产出数') + main_count_real = models.IntegerField('实际产出数', default=0) + steps = models.JSONField('工艺步骤', default=list) + state = models.IntegerField('状态', default=0) start_date_real = models.DateField('实际开工日期', null=True, blank=True) end_date_real = models.DateField('实际完工日期', null=True, blank=True) @@ -65,6 +72,8 @@ class SubProductionProgress(BaseModel): ) 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) - count = models.FloatField('应出入数') - count_real = models.IntegerField('实际出入数', default=0) + count = models.IntegerField('应出入数') + count_pick = models.IntegerField('实际领用数', default=0) + count_real = models.IntegerField('实际消耗/产出数', default=0) diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 8b170a5..735b0cc 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -1,7 +1,7 @@ from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from rest_framework import serializers from apps.sam.serializers import OrderSerializer -from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer +from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, SubProductionSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer @@ -27,10 +27,15 @@ class ResourceCalListSerializer(serializers.ListSerializer): class SubProductionPlanListSerializer(serializers.ModelSerializer): workshop_ = OrganizationSimpleSerializer(source='workshop', read_only=True) process_ = ProcessSimpleSerializer(source='process', read_only=True) + subproduction_ = SubProductionSimpleSerializer(source='subproduction', read_only=True) + main_product_ = MaterialSimpleSerializer(source='main_product', read_only=True) class Meta: model=SubProductionPlan fields = '__all__' + + + class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): class Meta: model = SubProductionPlan diff --git a/hb_server/apps/pm/signals.py b/hb_server/apps/pm/signals.py new file mode 100644 index 0000000..69aa69c --- /dev/null +++ b/hb_server/apps/pm/signals.py @@ -0,0 +1,18 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from apps.pm.models import SubProductionProgress + +@receiver(post_save, sender=SubProductionProgress) +def update_subplan_main(sender, instance, created, **kwargs): + """ + 根据生产进度统计表更新子计划进度表相关字段 + """ + if instance.is_main: + subplan = instance.subproduction_plan + if created: + subplan.main_product = instance.material + subplan.main_count = instance.count + subplan.main_count_real = instance.count_real + subplan.save() + + diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index ac35f84..c646ab6 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -87,8 +87,11 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel 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)) + # 生成子计划物料需求/进度 for m in SubprodctionMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): - SubProductionProgress.objects.create(material=m.material, type=m.type, count=m.count*production_plan.count, subproduction_plan=instance) + spro = SubProductionProgress.objects.create(material=m.material, type=m.type, + is_main=m.is_main, + count=m.count*production_plan.count, subproduction_plan=instance) production_plan.is_planed=True production_plan.save() return Response() @@ -98,10 +101,10 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo 子生产计划-列表/修改 """ perms_map = {'*': '*'} - queryset = SubProductionPlan.objects.select_related('process', 'workshop') + queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'main_product') search_fields = [] serializer_class = SubProductionPlanListSerializer - filterset_fields = ['production_plan', 'process', 'state'] + filterset_fields = ['production_plan', 'process', 'state', 'main_product', 'workshop'] ordering_fields = ['process__number'] ordering = ['process__number'] diff --git a/hb_server/apps/wpm/migrations/0004_auto_20211104_0914.py b/hb_server/apps/wpm/migrations/0004_auto_20211104_0914.py new file mode 100644 index 0000000..85eb696 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0004_auto_20211104_0914.py @@ -0,0 +1,61 @@ +# Generated by Django 3.2.6 on 2021-11-04 01:14 + +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 = [ + ('pm', '0010_auto_20211103_1700'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('mtm', '0028_auto_20211102_1707'), + ('wpm', '0003_auto_20211102_0935'), + ] + + operations = [ + migrations.CreateModel( + name='WProductRecord', + 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='删除标记')), + ('data', models.JSONField(blank=True, default=dict, verbose_name='记录的数据')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('record_form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用表格')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='wmaterial', + name='process', + ), + migrations.RemoveField( + model_name='wmaterial', + name='workshop', + ), + migrations.RemoveField( + model_name='wproductflow', + name='wproduct', + ), + migrations.AddField( + model_name='wmaterial', + name='subproduction_plan', + field=models.ForeignKey(default=68, on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子计划'), + preserve_default=False, + ), + migrations.AddField( + model_name='wproductflow', + name='wproducts', + field=models.JSONField(default=list, verbose_name='关联产品'), + ), + migrations.DeleteModel( + name='WProductForm', + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 97dfa62..31bd224 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -12,8 +12,9 @@ class WMaterial(BaseModel): """ 车间生产物料 """ - workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) - process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) + # 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) count = models.IntegerField('当前数量', default=0) @@ -36,7 +37,7 @@ 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) -class WProductForm(CommonAModel): +class WProductRecord(CommonAModel): """ 记录表格 """ @@ -46,6 +47,6 @@ class WProductForm(CommonAModel): class WProductFlow(BaseModel): """ - 产品流转日志 + 生产操作日志 """ - wproduct = models.ForeignKey(WProduct, verbose_name='产品', on_delete=models.CASCADE) \ No newline at end of file + wproducts = models.JSONField('关联产品', default=list) \ No newline at end of file diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index bec720e..b70d745 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse from apps.inm.signals import update_inm -from apps.mtm.models import Material +from apps.mtm.models import Material, Step from apps.mtm.serializers import MaterialSimpleSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress @@ -23,6 +23,8 @@ class PickSerializer(serializers.Serializer): def create(self, validated_data): picks = validated_data.pop('picks') sp = validated_data['subproduction_plan'] + if sp.state not in [1,2]: + raise serializers.ValidationError('该子计划状态错误') if sp.is_picked: raise serializers.ValidationError('该子计划已领料') for i in picks: @@ -46,20 +48,21 @@ class PickSerializer(serializers.Serializer): FIFODetail.objects.create(**i) # 更新车间物料 wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ - process=sp.process,defaults={ + subproduction_plan=sp,defaults={ 'material':i['material'], 'batch':i['batch'], - 'process':sp.process, - 'workshop':sp.workshop, + 'subproduction_plan':sp, 'count':0 }) wm.count = wm.count + i['count'] wm.save() # 更新子计划物料情况 spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1) - spp.count_real = spp.count_real + i['count'] + spp.count_pick = spp.count_pick + i['count'] spp.save() sp.is_picked=True + sp.state = 3 #生产中 + sp.state_date_real = timezone.now() #实际开工日期 sp.save() # 更新库存 fifo.is_audited = True @@ -74,4 +77,8 @@ class WMaterialListSerializer(serializers.ModelSerializer): material_ = MaterialSimpleSerializer(source='material', read_only=True) class Meta: model = WMaterial - fields = '__all__' \ No newline at end of file + fields = '__all__' + + +class DoFormInitSerializer(serializers.Serializer): + action = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all()) \ No newline at end of file diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index d4685ec..d6aad5e 100644 --- a/hb_server/apps/wpm/urls.py +++ b/hb_server/apps/wpm/urls.py @@ -3,10 +3,11 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.wpm.views import WMaterialViewSet +from apps.wpm.views import WMaterialViewSet, WPlanViewSet router = DefaultRouter() router.register('wmaterial', WMaterialViewSet, basename='wmaterial') +router.register('subplan', WPlanViewSet, basename='wplan') urlpatterns = [ path('', include(router.urls)), ] diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 8a3dc4e..74ee648 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -3,7 +3,8 @@ from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.mixins import ListModelMixin from rest_framework.utils.field_mapping import get_relation_kwargs from rest_framework.viewsets import GenericViewSet, ModelViewSet -from apps.pm.serializers import SubProductionPlanUpdateSerializer +from apps.pm.models import SubProductionPlan +from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action @@ -12,6 +13,18 @@ from apps.wpm.models import WMaterial from apps.wpm.serializers import PickSerializer, WMaterialListSerializer from rest_framework.response import Response # Create your views here. +class WPlanViewSet(ListModelMixin, GenericViewSet): + """ + 车间生产计划 + """ + perms_map = {'*': '*'} + queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'main_product').exclude(state=0) + search_fields = [] + serializer_class = SubProductionPlanListSerializer + filterset_fields = ['production_plan', 'process', 'state', 'main_product', 'workshop'] + ordering_fields = ['process__number'] + ordering = ['process__number'] + class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): """ 车间物料表 @@ -19,7 +32,7 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): perms_map={'*':'*'} queryset = WMaterial.objects.select_related('material').all() serializer_class = WMaterialListSerializer - filterset_fields = ['material', 'process', 'workshop'] + filterset_fields = ['material', 'subproduction_plan', 'subproduction_plan__process', 'subproduction_plan__workshop'] ordering_fields = ['material__number'] ordering = ['material__number'] diff --git a/hb_server/utils/model.py b/hb_server/utils/model.py index 05d04ab..fb7fe70 100644 --- a/hb_server/utils/model.py +++ b/hb_server/utils/model.py @@ -2,9 +2,8 @@ from django.db import models import django.utils.timezone as timezone from django.db.models.query import QuerySet + # 自定义软删除查询基类 - - class SoftDeletableQuerySetMixin(object): ''' QuerySet for SoftDeletableModel. Instead of removing instance sets @@ -63,6 +62,24 @@ class BaseModel(models.Model): class Meta: abstract = True + def save(self, *args, **kwargs): + if self.pk: + # If self.pk is not None then it's an update. + cls = self.__class__ + old = cls.objects.get(pk=self.pk) + # This will get the current model state since super().save() isn't called yet. + new = self # This gets the newly instantiated Mode object with the new values. + changed_fields = [] + for field in cls._meta.get_fields(): + field_name = field.name + try: + if getattr(old, field_name) != getattr(new, field_name): + changed_fields.append(field_name) + except Exception as ex: # Catch field does not exist exception + pass + kwargs['update_fields'] = changed_fields + super().save(*args, **kwargs) + class SoftModel(BaseModel): """ 软删除基本表 From ba9466a90b0af720c05007d506c68b789fc432ac Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 4 Nov 2021 10:07:41 +0800 Subject: [PATCH 02/23] =?UTF-8?q?=E7=94=9F=E6=88=90=E5=AD=90=E8=AE=A1?= =?UTF-8?q?=E5=88=92bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pm/migrations/0011_auto_20211104_1006.py | 25 +++++++++++++++++++ hb_server/apps/pm/models.py | 6 ++--- hb_server/apps/wpm/models.py | 20 ++++++++------- 3 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 hb_server/apps/pm/migrations/0011_auto_20211104_1006.py diff --git a/hb_server/apps/pm/migrations/0011_auto_20211104_1006.py b/hb_server/apps/pm/migrations/0011_auto_20211104_1006.py new file mode 100644 index 0000000..fafca63 --- /dev/null +++ b/hb_server/apps/pm/migrations/0011_auto_20211104_1006.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.6 on 2021-11-04 02:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ('pm', '0010_auto_20211103_1700'), + ] + + operations = [ + migrations.AlterField( + model_name='subproductionplan', + name='main_count', + field=models.IntegerField(default=0, verbose_name='应产出数'), + ), + migrations.AlterField( + model_name='subproductionplan', + name='main_product', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='主要产品'), + ), + ] diff --git a/hb_server/apps/pm/models.py b/hb_server/apps/pm/models.py index f528c64..62b02e8 100644 --- a/hb_server/apps/pm/models.py +++ b/hb_server/apps/pm/models.py @@ -46,9 +46,9 @@ class SubProductionPlan(CommonAModel): workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) - - main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE) - main_count = models.IntegerField('应产出数') + + main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True) + main_count = models.IntegerField('应产出数', default=0) main_count_real = models.IntegerField('实际产出数', default=0) steps = models.JSONField('工艺步骤', default=list) diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 31bd224..57d9548 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -37,16 +37,18 @@ 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) -class WProductRecord(CommonAModel): - """ - 记录表格 - """ - record_form = models.ForeignKey(RecordForm, verbose_name='所用表格', on_delete=models.CASCADE) - data = models.JSONField('记录的数据', default=dict, blank=True) - - class WProductFlow(BaseModel): """ 生产操作日志 """ - wproducts = models.JSONField('关联产品', default=list) \ No newline at end of file + wproducts = models.JSONField('关联产品ID列表', default=list, blank=True) + 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 WProductRecord(CommonAModel): + """ + 记录表格 + """ + form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE) + record_data = models.JSONField('记录的数据', default=dict, blank=True) + wproduct_flow = models.ForeignKey(WProductFlow, verbose_name='关联的生产操作日志', on_delete=models.CASCADE) From 1b7190a4b6c8aa03ad29e78fb54e7377908ca72c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 4 Nov 2021 16:14:48 +0800 Subject: [PATCH 03/23] =?UTF-8?q?=E8=B0=83=E7=94=A8=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 4 +-- hb_server/apps/wpm/models.py | 34 ++++++++++++++++------ hb_server/apps/wpm/serializers.py | 8 ++++-- hb_server/apps/wpm/urls.py | 3 +- hb_server/apps/wpm/views.py | 47 ++++++++++++++++++++++++++----- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 2c3bd49..f8b7f5d 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render from rest_framework import serializers from rest_framework.exceptions import APIException -from rest_framework.mixins import ListModelMixin, RetrieveModelMixin +from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin from rest_framework.viewsets import GenericViewSet, ModelViewSet from apps.inm.filters import MbFilterSet @@ -63,7 +63,7 @@ class MaterialBatchViewSet(ListModelMixin, GenericViewSet): queryset = self.queryset.filter(warehouse__id=data['warehouse'], material__id__in=data['materials']) return Response(MaterialBatchSerializer(instance=queryset, many=True).data) -class FIFODetailViewSet(ListModelMixin, GenericViewSet): +class FIFODetailViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): """ 出入库记录详情表 """ diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 57d9548..a7e46a1 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -24,31 +24,47 @@ class WProduct(CommonAModel): 半成品/成品 """ act_state_choices=( - (0, '待执行'), - (1, '进行中'), - (2, '已完成') + (1, '生产中'), + (2, '待检测'), + (3, '已合格') ) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True) - act_state = models.IntegerField('进行状态', default=0) - parent = models.ForeignKey('self', verbose_name='上一级', on_delete=models.CASCADE, db_constraint=False) + 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) 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 WProductFlow(BaseModel): + +class WProductAction(CommonAModel): """ - 生产操作日志 + 生产操作 """ wproducts = models.JSONField('关联产品ID列表', default=list, blank=True) - p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True) + m_state = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE) + 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): + """ + 车间生产物料消耗产出表 + """ + type_choices=( + (1, '消耗'), + (2, '产出') + ) + type = models.IntegerField('类型', default=0, choices=type_choices) + wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) + material = models.ForeignKey(Material, verbose_name='关联的物料', on_delete=models.CASCADE) + count = models.IntegerField('消耗或产出数量') + class WProductRecord(CommonAModel): """ 记录表格 """ form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE) record_data = models.JSONField('记录的数据', default=dict, blank=True) - wproduct_flow = models.ForeignKey(WProductFlow, verbose_name='关联的生产操作日志', on_delete=models.CASCADE) + wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index b70d745..e842a4d 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,7 +8,7 @@ from apps.mtm.serializers import MaterialSimpleSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from django.utils import timezone -from apps.wpm.models import WMaterial +from apps.wpm.models import WMaterial, WProduct class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -45,6 +45,7 @@ class PickSerializer(serializers.Serializer): # 更新出库详情 i['fifo'] = fifo i['count'] = i.pop('pick_count') + i['is_teskok'] = True FIFODetail.objects.create(**i) # 更新车间物料 wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ @@ -81,4 +82,7 @@ class WMaterialListSerializer(serializers.ModelSerializer): class DoFormInitSerializer(serializers.Serializer): - action = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all()) \ No newline at end of file + step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") + subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID", required=False) + wproducts = serializers.ListField(child= + serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False) diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index d6aad5e..1ce7fa3 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 WMaterialViewSet, WPlanViewSet +from apps.wpm.views import DoFormInit, WMaterialViewSet, WPlanViewSet router = DefaultRouter() router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('subplan', WPlanViewSet, basename='wplan') urlpatterns = [ + path('do/init/', DoFormInit.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 74ee648..ac6bba3 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -2,15 +2,18 @@ from django.shortcuts import render from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.mixins import ListModelMixin 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.pm.models import SubProductionPlan +from apps.mtm.models import RecordForm +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 -from apps.wpm.serializers import PickSerializer, WMaterialListSerializer +from apps.wpm.serializers import DoFormInitSerializer, PickSerializer, WMaterialListSerializer from rest_framework.response import Response # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): @@ -46,8 +49,38 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): serializer.save() return Response() -class DoFormInit(CreateAPIView): - """ - 生产操作表单创建 - """ - perms_map={'*':'*'} \ No newline at end of file +class DoFormInit(APIView): + perms_map={'*':'*'} + + def post(self, request, format=None): + """ + 调用操作表单 + """ + serializer = DoFormInitSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + ret = {} + # 调出该子计划现有物料 + ret['input'] = list(WMaterial.objects.filter(subproduction_plan=vdata['subproduction_plan'])\ + .values('material', 'material__name', 'count')) + for i in ret['input']: + i['count_input'] = 0 + # 需要输出的物料 + # 如果传入半成品列表就不需要 + if not 'wproducts' in vdata: + ret['output'] = list(SubProductionProgress.objects.filter(subproduction_plan=vdata['subproduction_plan'], type=2)\ + .values('material', 'material__name')) + for i in ret['output']: + i['count_output']=0 + ret['forms'] = [] + forms = RecordForm.objects.filter(step=vdata['step'], type=1) + if forms.exists(): + ret['forms'] = RecordFormDetailSerializer(instance=forms, many=True).data + return Response(ret) + + +class DoFormSubmit(APIView): + def post(self, request, format=None): + """ + 提交操作表单 + """ \ No newline at end of file From 9588562390df0c3d8a47a094c049bbda17f9f042 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 09:03:15 +0800 Subject: [PATCH 04/23] =?UTF-8?q?=E7=94=9F=E4=BA=A7=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/views.py | 5 + hb_server/apps/qm/models.py | 18 ++- .../wpm/migrations/0005_auto_20211108_0901.py | 114 ++++++++++++++++ hb_server/apps/wpm/models.py | 22 ++- hb_server/apps/wpm/serializers.py | 41 +++++- hb_server/apps/wpm/urls.py | 3 +- hb_server/apps/wpm/views.py | 126 ++++++++++++++++-- 7 files changed, 305 insertions(+), 24 deletions(-) create mode 100644 hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index f8b7f5d..10eb4b5 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -75,6 +75,11 @@ class FIFODetailViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): ordering_fields = ['create_time'] ordering = ['-create_time'] + def perform_destroy(self, instance): + if instance.fifo.is_audited: + raise APIException('该出入库记录已通过审核, 无法删除') + return super().perform_destroy(instance) + class FIFOViewSet(ListModelMixin, GenericViewSet): """ 出入库记录 diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index 062f5c9..5da4930 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -1,5 +1,8 @@ from django.db import models +from django.db.models.enums import Choices +from apps.mtm.models import RecordForm, RecordFormField from apps.system.models import CommonAModel, File +from utils.model import BaseModel # Create your models here. class Standard(CommonAModel): """ @@ -49,4 +52,17 @@ class TestRecord(CommonAModel): record_data = models.JSONField('记录数据', default=dict, blank=True) is_testok = models.BooleanField('是否合格', default=True) fifo_detail = models.ForeignKey('inm.fifodetail', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True) - \ No newline at end of file + + +class TestRecordDetail(BaseModel): + """ + 记录表格字段值 + """ + form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False) + field_name = models.CharField('字段名', max_length=50) + field_key = models.CharField('字段标识', max_length=50) + field_type = models.IntegerField('字段类型', choices=RecordForm.type_choices) + field_value = models.JSONField('录入值', default=dict, blank=True) + need_judge = models.BooleanField('是否需要判定', default=False) + is_testok = models.BooleanField('是否合格', default=True) + test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE) \ No newline at end of file diff --git a/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py b/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py new file mode 100644 index 0000000..174fc1d --- /dev/null +++ b/hb_server/apps/wpm/migrations/0005_auto_20211108_0901.py @@ -0,0 +1,114 @@ +# Generated by Django 3.2.6 on 2021-11-08 01:01 + +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), + ('mtm', '0028_auto_20211102_1707'), + ('wpm', '0004_auto_20211104_0914'), + ] + + operations = [ + migrations.CreateModel( + name='WProductAction', + 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='wproductaction_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='wproductaction_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='WProductMaterial', + 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='可能产出的副产品')), + ('wmaterial', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wmaterial', verbose_name='关联的车间物料')), + ('wproduct_action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductaction', verbose_name='关联的生产操作')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='WProductRecordDetail', + 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.IntegerField(choices=[(1, '生产记录'), (2, '检验记录')], 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='关联字段')), + ], + options={ + 'abstract': False, + }, + ), + migrations.DeleteModel( + name='WProductFlow', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='data', + ), + migrations.RemoveField( + model_name='wproductrecord', + name='record_form', + ), + migrations.AddField( + model_name='wproduct', + name='is_executed', + field=models.BooleanField(default=False, verbose_name='子工序是否已执行'), + ), + migrations.AddField( + model_name='wproductrecord', + name='form', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用的生产记录表格'), + preserve_default=False, + ), + migrations.AlterField( + model_name='wproduct', + 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', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductrecord', verbose_name='关联的生产记录'), + ), + migrations.AddField( + model_name='wproductrecord', + name='wproduct_action', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductaction', verbose_name='关联的生产操作'), + preserve_default=False, + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index a7e46a1..8566f93 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -6,7 +6,7 @@ from apps.pm.models import ProductionPlan, SubProductionPlan from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File from utils.model import SoftModel, BaseModel from simple_history.models import HistoricalRecords -from apps.mtm.models import Material, Process, Step, RecordForm +from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm class WMaterial(BaseModel): """ @@ -44,7 +44,7 @@ class WProductAction(CommonAModel): 生产操作 """ wproducts = models.JSONField('关联产品ID列表', default=list, blank=True) - m_state = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE) + m_state = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE, null=True, blank=True) 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) @@ -58,7 +58,8 @@ class WProductMaterial(BaseModel): ) type = models.IntegerField('类型', default=0, choices=type_choices) wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) - material = models.ForeignKey(Material, 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): @@ -66,5 +67,18 @@ class WProductRecord(CommonAModel): 记录表格 """ form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE) - record_data = models.JSONField('记录的数据', default=dict, blank=True) wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) + + +class WProductRecordDetail(BaseModel): + """ + 记录表格字段值 + """ + form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False) + field_name = models.CharField('字段名', max_length=50) + field_key = models.CharField('字段标识', max_length=50) + field_type = models.IntegerField('字段类型', choices=RecordForm.type_choices) + field_value = models.JSONField('录入值', default=dict, blank=True) + sort = models.IntegerField('排序号', default=1) + wproduct_record = models.ForeignKey(WProductRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE) + diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index e842a4d..23afaa2 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -2,13 +2,13 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse from apps.inm.signals import update_inm -from apps.mtm.models import Material, Step +from apps.mtm.models import Material, RecordForm, Step 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 +from apps.wpm.models import WMaterial, WProduct, WProductRecord class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -81,8 +81,41 @@ class WMaterialListSerializer(serializers.ModelSerializer): fields = '__all__' -class DoFormInitSerializer(serializers.Serializer): +class WActionInitSerializer(serializers.Serializer): step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") - subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID", required=False) + subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") wproducts = serializers.ListField(child= serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False) + +class DoInputSerializer(serializers.Serializer): + id = serializers.PrimaryKeyRelatedField(queryset=WMaterial.objects.all(), label='车间物料ID') + count_input = serializers.IntegerField(min_value=0, label='消耗数量') + +class DoOutputSerializer(serializers.Serializer): + material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label='物料ID') + count_output = serializers.IntegerField(min_value=0, label='产出数量') + +class WProductRecordDetailSerializer(serializers.ModelSerializer): + class Meta: + model = WProductRecord + fields = ['form_field', 'field_value'] + +class WProductRecordSerializer(serializers.ModelSerializer): + record_data = WProductRecordDetailSerializer(many=True) + class Meta: + model = WProductRecord + fields = ['form', 'record_data'] + + + +class WActionSubmitSerializer(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) + + + \ No newline at end of file diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index 1ce7fa3..89604b5 100644 --- a/hb_server/apps/wpm/urls.py +++ b/hb_server/apps/wpm/urls.py @@ -3,13 +3,14 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.wpm.views import DoFormInit, WMaterialViewSet, WPlanViewSet +from apps.wpm.views import DoFormInit, DoFormSubmit, WMaterialViewSet, WPlanViewSet router = DefaultRouter() router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('subplan', WPlanViewSet, basename='wplan') urlpatterns = [ path('do/init/', DoFormInit.as_view()), + path('do/submit/', DoFormSubmit.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index ac6bba3..bf4fe1b 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -1,19 +1,20 @@ from django.shortcuts import render from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.mixins import ListModelMixin +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 RecordForm +from apps.mtm.models import Material, RecordForm, Step 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 +from apps.wpm.models import WMaterial, WProduct, WProductAction, WProductMaterial, WProductRecord, WProductRecordDetail -from apps.wpm.serializers import DoFormInitSerializer, PickSerializer, WMaterialListSerializer +from apps.wpm.serializers import PickSerializer, WActionInitSerializer, WActionSubmitSerializer, WMaterialListSerializer from rest_framework.response import Response # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): @@ -49,29 +50,42 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): serializer.save() return Response() -class DoFormInit(APIView): +class DoFormInit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} - + serializer_class=WActionInitSerializer def post(self, request, format=None): """ 调用操作表单 """ - serializer = DoFormInitSerializer(data=request.data) + data = request.data + serializer = WActionInitSerializer(data=data) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data ret = {} + ret['step'] = data['step'] + ret['subproduction_plan'] = data['subproduction_plan'] + if 'wproducts' in data and data['wproducts']: + ret['wproducts'] = data['wproducts'] + else: + ret['wproducts'] = [] # 调出该子计划现有物料 ret['input'] = list(WMaterial.objects.filter(subproduction_plan=vdata['subproduction_plan'])\ - .values('material', 'material__name', 'count')) + .values('id', 'material', 'material__name', 'count', 'material__number', 'batch')) for i in ret['input']: i['count_input'] = 0 # 需要输出的物料 # 如果传入半成品列表就不需要 - if not 'wproducts' in vdata: - ret['output'] = list(SubProductionProgress.objects.filter(subproduction_plan=vdata['subproduction_plan'], type=2)\ - .values('material', 'material__name')) - for i in ret['output']: - i['count_output']=0 + if ret['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) + else: + o_objs = SubProductionProgress.objects.filter( + subproduction_plan=vdata['subproduction_plan'], type=2) + ret['output'] = list(o_objs.values('material', 'material__name', 'material__number')) + for i in ret['output']: + i['count_output']=0 ret['forms'] = [] forms = RecordForm.objects.filter(step=vdata['step'], type=1) if forms.exists(): @@ -79,8 +93,92 @@ class DoFormInit(APIView): return Response(ret) -class DoFormSubmit(APIView): +class DoFormSubmit(CreateAPIView, GenericAPIView): + perms_map={'*':'*'} + serializer_class = WActionSubmitSerializer def post(self, request, format=None): """ 提交操作表单 - """ \ No newline at end of file + """ + data = request.data + serializer = WActionInitSerializer(data=data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data #校验之后的数据 + # 创建一个生产操作记录 + action_obj = WProductAction() + action_obj.p_state = vdata['step'] + if 'wproducts' in data and data['wproducts']: + action_obj.wproducts = data['wproducts'] + action_obj.m_state = vdata[0].m_state + action_obj.remark = data['remark'] # 操作备注 + action_obj.create_by = request.user + action_obj.save() + + # 保存物料消耗 + for i in vdata['input']: + if i['count_input']: #如果有消耗 + i_wmat = i['id'] + WProductMaterial.objects.create(type=1, wproduct_action=action_obj, + wmaterial= i_wmat, count=i['count_input']) + # 更新车间物料 + i_wmat.count = i_wmat.count- i['count_input'] + i_wmat.save() + # 更新子计划物料消耗情况 + sp = SubProductionProgress.objects.get(subproduction_plan=i_wmat.subproduction_plan, + material=i_wmat.material) + sp.count_real = sp.count_real + i['count_input'] + sp.save() + + # 物料产出 + if 'output' in data and data['output']: + for i in vdata['output']: # 已经序列化好的数据 + ma = i['material'] + if ma.is_main: + # 计划开始, 第一步切割创建动态产品 + wpr = dict(m_state=ma, p_state=vdata['step'], + act_state=1, 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) + else: + # 更新操作消耗物料表 + WProductMaterial.objects.create(type=2, wproduct_action=action_obj, + material= ma, count=i['count_output']) + # 更新车间物料表 + ins, _ = WMaterial.objects.get_or_create(subproduction_plan=vdata['subproduction_plan'], + material=ma) + ins.count = ins.count + i['count_output'] + ins.save() + # 更新子计划进度表 + sp = SubProductionProgress.objects.get(subproduction_plan=vdata['subproduction_plan'], + material=ma) + 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) + + # 保存自定义表单结果 + for i in vdata['forms']: + wr = WProductRecord() + wr.form = i['form'] + wr.create_by = request.user + wr.wproduct_action = action_obj + wr.save() + wrds = [] + for m in i['record_data']: # 保存记录详情 + form_field = m['form_field'] + m['field_name'] = form_field.field_name + m['field_key'] = form_field.field_key + m['field_type'] = form_field.field_type + m['field_value'] = form_field.field_value + m['sort'] = form_field.sort + m['wproduct_record'] = wr + wrds.append(WProductRecordDetail(**m)) + WProductRecordDetail.objects.bulk_create(wrds) + return Response() + + \ No newline at end of file From c5aafb79e0ac03d42eb2258abe38102c591567f4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 09:05:03 +0800 Subject: [PATCH 05/23] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 23afaa2..876d18e 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,7 +8,7 @@ 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 +from apps.wpm.models import WMaterial, WProduct, WProductRecord, WProductRecordDetail class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -97,7 +97,7 @@ class DoOutputSerializer(serializers.Serializer): class WProductRecordDetailSerializer(serializers.ModelSerializer): class Meta: - model = WProductRecord + model = WProductRecordDetail fields = ['form_field', 'field_value'] class WProductRecordSerializer(serializers.ModelSerializer): From b966714f7bb7d0a831025f75a57e3458bad0b737 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 09:27:10 +0800 Subject: [PATCH 06/23] =?UTF-8?q?action=20form=20init=20=E7=BB=84=E8=A3=85?= =?UTF-8?q?form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/views.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index bf4fe1b..c5ff876 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -62,20 +62,21 @@ class DoFormInit(CreateAPIView, GenericAPIView): serializer.is_valid(raise_exception=True) vdata = serializer.validated_data ret = {} - ret['step'] = data['step'] - ret['subproduction_plan'] = data['subproduction_plan'] + ret_0 = {} + ret_0['step'] = data['step'] + ret_0['subproduction_plan'] = data['subproduction_plan'] if 'wproducts' in data and data['wproducts']: - ret['wproducts'] = data['wproducts'] + ret_0['wproducts'] = data['wproducts'] else: - ret['wproducts'] = [] + ret_0['wproducts'] = [] # 调出该子计划现有物料 - ret['input'] = list(WMaterial.objects.filter(subproduction_plan=vdata['subproduction_plan'])\ + ret_0['input'] = list(WMaterial.objects.filter(subproduction_plan=vdata['subproduction_plan'])\ .values('id', 'material', 'material__name', 'count', 'material__number', 'batch')) - for i in ret['input']: + for i in ret_0['input']: i['count_input'] = 0 # 需要输出的物料 # 如果传入半成品列表就不需要 - if ret['wproducts']: + if ret_0['wproducts']: # 排除wproduct列表 # mids = WProduct.objects.filter(pk__in=data['wproducts']).values_list('m_state', flat=True) o_objs = SubProductionProgress.objects.filter( @@ -83,13 +84,16 @@ class DoFormInit(CreateAPIView, GenericAPIView): else: o_objs = SubProductionProgress.objects.filter( subproduction_plan=vdata['subproduction_plan'], type=2) - ret['output'] = list(o_objs.values('material', 'material__name', 'material__number')) - for i in ret['output']: + ret_0['output'] = list(o_objs.values('material', 'material__name', 'material__number')) + for i in ret_0['output']: i['count_output']=0 ret['forms'] = [] + ret_0['id'] = 0 + ret_0['name'] = '基本信息' + ret['forms'].append(ret_0) forms = RecordForm.objects.filter(step=vdata['step'], type=1) if forms.exists(): - ret['forms'] = RecordFormDetailSerializer(instance=forms, many=True).data + ret['forms'].extend(RecordFormDetailSerializer(instance=forms, many=True).data) return Response(ret) From 950418a1544add8ec521d8e0f5e1ab314d176fac Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 09:35:53 +0800 Subject: [PATCH 07/23] =?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_server/apps/qm/models.py | 2 +- ..._wproductrecorddetail_wproductrecorditem.py | 18 ++++++++++++++++++ hb_server/apps/wpm/models.py | 2 +- hb_server/apps/wpm/serializers.py | 8 ++++---- hb_server/apps/wpm/views.py | 6 +++--- 5 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 hb_server/apps/wpm/migrations/0006_rename_wproductrecorddetail_wproductrecorditem.py diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index 5da4930..2d5a917 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -54,7 +54,7 @@ class TestRecord(CommonAModel): fifo_detail = models.ForeignKey('inm.fifodetail', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True) -class TestRecordDetail(BaseModel): +class TestRecordItem(BaseModel): """ 记录表格字段值 """ diff --git a/hb_server/apps/wpm/migrations/0006_rename_wproductrecorddetail_wproductrecorditem.py b/hb_server/apps/wpm/migrations/0006_rename_wproductrecorddetail_wproductrecorditem.py new file mode 100644 index 0000000..b1346c6 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0006_rename_wproductrecorddetail_wproductrecorditem.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 01:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ('wpm', '0005_auto_20211108_0901'), + ] + + operations = [ + migrations.RenameModel( + old_name='WProductRecordDetail', + new_name='WProductRecordItem', + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 8566f93..a930c82 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -70,7 +70,7 @@ class WProductRecord(CommonAModel): wproduct_action = models.ForeignKey(WProductAction, verbose_name='关联的生产操作', on_delete=models.CASCADE) -class WProductRecordDetail(BaseModel): +class WProductRecordItem(BaseModel): """ 记录表格字段值 """ diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 876d18e..035fa4a 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,7 +8,7 @@ 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, WProductRecordDetail +from apps.wpm.models import WMaterial, WProduct, WProductRecord, WProductRecordItem class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -95,13 +95,13 @@ class DoOutputSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label='物料ID') count_output = serializers.IntegerField(min_value=0, label='产出数量') -class WProductRecordDetailSerializer(serializers.ModelSerializer): +class WProductRecordItemSerializer(serializers.ModelSerializer): class Meta: - model = WProductRecordDetail + model = WProductRecordItem fields = ['form_field', 'field_value'] class WProductRecordSerializer(serializers.ModelSerializer): - record_data = WProductRecordDetailSerializer(many=True) + record_data = WProductRecordItemSerializer(many=True) class Meta: model = WProductRecord fields = ['form', 'record_data'] diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index c5ff876..828ac89 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -12,7 +12,7 @@ from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPl from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action -from apps.wpm.models import WMaterial, WProduct, WProductAction, WProductMaterial, WProductRecord, WProductRecordDetail +from apps.wpm.models import WMaterial, WProduct, WProductAction, WProductMaterial, WProductRecord, WProductRecordItem from apps.wpm.serializers import PickSerializer, WActionInitSerializer, WActionSubmitSerializer, WMaterialListSerializer from rest_framework.response import Response @@ -181,8 +181,8 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): m['field_value'] = form_field.field_value m['sort'] = form_field.sort m['wproduct_record'] = wr - wrds.append(WProductRecordDetail(**m)) - WProductRecordDetail.objects.bulk_create(wrds) + wrds.append(WProductRecordItem(**m)) + WProductRecordItem.objects.bulk_create(wrds) return Response() \ No newline at end of file From 5357a2d82a3fd93fe2a41a37229c57aef7a51689 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 09:53:16 +0800 Subject: [PATCH 08/23] =?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 --- .../0011_rename_fifodetail_fifoitem.py | 19 +++++++++ hb_server/apps/inm/models.py | 4 +- hb_server/apps/inm/serializers.py | 12 +++--- hb_server/apps/inm/signals.py | 6 +-- hb_server/apps/inm/urls.py | 4 +- hb_server/apps/inm/views.py | 12 +++--- .../qm/migrations/0004_auto_20211108_0951.py | 41 +++++++++++++++++++ hb_server/apps/qm/models.py | 2 +- hb_server/apps/qm/serializers.py | 10 ++++- hb_server/apps/qm/views.py | 10 ++--- hb_server/apps/wpm/serializers.py | 4 +- 11 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 hb_server/apps/inm/migrations/0011_rename_fifodetail_fifoitem.py create mode 100644 hb_server/apps/qm/migrations/0004_auto_20211108_0951.py diff --git a/hb_server/apps/inm/migrations/0011_rename_fifodetail_fifoitem.py b/hb_server/apps/inm/migrations/0011_rename_fifodetail_fifoitem.py new file mode 100644 index 0000000..46f6dc7 --- /dev/null +++ b/hb_server/apps/inm/migrations/0011_rename_fifodetail_fifoitem.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.6 on 2021-11-08 01:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ('qm', '0003_testrecord'), + ('inm', '0010_auto_20211102_1631'), + ] + + operations = [ + migrations.RenameModel( + old_name='FIFODetail', + new_name='FIFOItem', + ), + ] diff --git a/hb_server/apps/inm/models.py b/hb_server/apps/inm/models.py index eefa002..fc21596 100644 --- a/hb_server/apps/inm/models.py +++ b/hb_server/apps/inm/models.py @@ -68,9 +68,9 @@ class FIFO(CommonAModel): remark = models.CharField('备注', max_length=1000, default='') -class FIFODetail(BaseModel): +class FIFOItem(BaseModel): """ - 出入库详细记录 + 出入库详细条目 """ is_tested = models.BooleanField('是否已检测', default=False) is_testok = models.BooleanField('是否检测合格', default=False) diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 0087fae..62461c4 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from apps.inm.models import FIFO, FIFODetail, IProduct, MaterialBatch, WareHouse,Inventory +from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory from apps.system.serializers import UserSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer @@ -42,10 +42,10 @@ class FIFOListSerializer(serializers.ModelSerializer): model=FIFO fields = '__all__' -class FIFODetailSerializer(serializers.ModelSerializer): +class FIFOItemSerializer(serializers.ModelSerializer): material_= MaterialSimpleSerializer(source='material', read_only=True) class Meta: - model=FIFODetail + model= FIFOItem fields = '__all__' class IProductInPurSerializer(serializers.ModelSerializer): @@ -56,7 +56,7 @@ class IProductInPurSerializer(serializers.ModelSerializer): class FIFODetailInPurSerializer(serializers.ModelSerializer): details = IProductInPurSerializer(many=True, required=False) class Meta: - model = FIFODetail + model = FIFOItem fields = ['material', 'count', 'batch', 'details'] class MaterialBatchQuerySerializer(serializers.Serializer): @@ -101,7 +101,7 @@ class FIFOInPurSerializer(serializers.ModelSerializer): raise serializers.ValidationError('数目对不上') else: i['fifo'] = obj - fifod = FIFODetail.objects.create(**i) + fifod = FIFOItem.objects.create(**i) p_list = [] for x in p_details: x['state'] = 1 @@ -113,6 +113,6 @@ class FIFOInPurSerializer(serializers.ModelSerializer): IProduct.objects.bulk_create(p_list) else: i['fifo'] = obj - FIFODetail.objects.create(**i) + FIFOItem.objects.create(**i) return obj diff --git a/hb_server/apps/inm/signals.py b/hb_server/apps/inm/signals.py index c4b74ee..f3fd13f 100644 --- a/hb_server/apps/inm/signals.py +++ b/hb_server/apps/inm/signals.py @@ -1,7 +1,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver -from apps.inm.models import Inventory, MaterialBatch, FIFO, FIFODetail +from apps.inm.models import Inventory, MaterialBatch, FIFO, FIFOItem def update_inm(instance:FIFO, type:int=1): @@ -11,7 +11,7 @@ def update_inm(instance:FIFO, type:int=1): warehouse = instance.warehouse if instance.type in [3]: # 采购入库 # 更新相关表 - for i in FIFODetail.objects.filter(fifo=instance): + for i in FIFOItem.objects.filter(fifo=instance): material = i.material o1, _ = Inventory.objects.get_or_create(material=material, warehouse=warehouse, \ defaults={'material':material, 'warehouse':warehouse, 'count':0}) @@ -25,7 +25,7 @@ def update_inm(instance:FIFO, type:int=1): material.save() elif instance.type in [1]: # 生产领料 # 更新相关表 - for i in FIFODetail.objects.filter(fifo=instance): + for i in FIFOItem.objects.filter(fifo=instance): material = i.material o1 = Inventory.objects.get(material=material, warehouse=warehouse) o1.count = o1.count - i.count diff --git a/hb_server/apps/inm/urls.py b/hb_server/apps/inm/urls.py index 4b968ff..f5d68de 100644 --- a/hb_server/apps/inm/urls.py +++ b/hb_server/apps/inm/urls.py @@ -1,6 +1,6 @@ from django.db.models import base from rest_framework import urlpatterns -from apps.inm.views import FIFODetailViewSet, FIFOViewSet, MaterialBatchViewSet, WarehouseViewSet,InventoryViewSet +from apps.inm.views import FIFOItemViewSet, FIFOViewSet, MaterialBatchViewSet, WarehouseViewSet,InventoryViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter @@ -9,7 +9,7 @@ router.register('warehouse', WarehouseViewSet, basename='warehouse') router.register('inventory', InventoryViewSet, basename='inventory') router.register('materialbatch', MaterialBatchViewSet, basename='materialbatch') router.register('fifo', FIFOViewSet, basename='fifo'), -router.register('fifodetail', FIFODetailViewSet, basename='fifodetail') +router.register('fifodetail', FIFOItemViewSet, basename='fifodetail') urlpatterns = [ path('', include(router.urls)), ] diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 10eb4b5..032124a 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -5,8 +5,8 @@ from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveMod from rest_framework.viewsets import GenericViewSet, ModelViewSet from apps.inm.filters import MbFilterSet -from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse,Inventory -from apps.inm.serializers import FIFODetailSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer +from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse,Inventory +from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer from apps.inm.signals import update_inm from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action @@ -63,13 +63,13 @@ class MaterialBatchViewSet(ListModelMixin, GenericViewSet): queryset = self.queryset.filter(warehouse__id=data['warehouse'], material__id__in=data['materials']) return Response(MaterialBatchSerializer(instance=queryset, many=True).data) -class FIFODetailViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): +class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet): """ 出入库记录详情表 """ perms_map = {'*': '*'} - queryset = FIFODetail.objects.select_related('material', 'fifo').all() - serializer_class = FIFODetailSerializer + queryset = FIFOItem.objects.select_related('material', 'fifo').all() + serializer_class = FIFOItemSerializer filterset_fields = ['material', 'fifo'] search_fields = [] ordering_fields = ['create_time'] @@ -113,7 +113,7 @@ class FIFOViewSet(ListModelMixin, GenericViewSet): 审核通过 """ obj = self.get_object() - for i in FIFODetail.objects.filter(fifo=obj): + for i in FIFOItem.objects.filter(fifo=obj): if not i.is_testok: raise APIException('未检验通过, 不可审核') if obj.is_audited: diff --git a/hb_server/apps/qm/migrations/0004_auto_20211108_0951.py b/hb_server/apps/qm/migrations/0004_auto_20211108_0951.py new file mode 100644 index 0000000..853a26e --- /dev/null +++ b/hb_server/apps/qm/migrations/0004_auto_20211108_0951.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.6 on 2021-11-08 01:51 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0028_auto_20211102_1707'), + ('qm', '0003_testrecord'), + ] + + operations = [ + migrations.RenameField( + model_name='testrecord', + old_name='fifo_detail', + new_name='fifo_item', + ), + migrations.CreateModel( + name='TestRecordItem', + 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.IntegerField(choices=[(1, '生产记录'), (2, '检验记录')], verbose_name='字段类型')), + ('field_value', models.JSONField(blank=True, default=dict, verbose_name='录入值')), + ('need_judge', models.BooleanField(default=False, verbose_name='是否需要判定')), + ('is_testok', models.BooleanField(default=True, verbose_name='是否合格')), + ('form_field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联字段')), + ('test_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.testrecord', verbose_name='关联的检测记录')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index 2d5a917..ac58211 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -51,7 +51,7 @@ class TestRecord(CommonAModel): form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE) record_data = models.JSONField('记录数据', default=dict, blank=True) is_testok = models.BooleanField('是否合格', default=True) - fifo_detail = models.ForeignKey('inm.fifodetail', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True) + fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True) class TestRecordItem(BaseModel): diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index 51d5cb7..8924a14 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from apps.mtm.models import RecordForm, RecordFormField from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer from apps.system.serializers import FileSimpleSerializer -from .models import Standard, TestItem, TestRecord +from .models import Standard, TestItem, TestRecord, TestRecordItem class StandardCreateUpdateSerializer(serializers.ModelSerializer): class Meta: @@ -35,10 +35,16 @@ class AnalysisItemSerializer(serializers.ModelSerializer): pass +class TestRecordItemSerializer(serializers.ModelSerializer): + class Meta: + model = TestRecordItem + fields = ['form_field', 'field_value'] + class TestRecordCreateSerializer(serializers.ModelSerializer): + record_data = TestRecordItemSerializer(many=True) class Meta: model = TestRecord - fields = ['form', 'record_data', 'is_testok', 'fifo_detail'] + fields = ['form', 'record_data', 'is_testok', 'fifo_item'] def create(self, validated_data): if 'is_testok' not in validated_data: diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index 41133f5..e43ec38 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -43,7 +43,7 @@ class TestRecordViewSet(ModelViewSet): 检测记录 """ perms_map = {'*': '*'} - queryset = TestRecord.objects.select_related('fifo_detail', 'form').all() + queryset = TestRecord.objects.select_related('fifo_item', 'form').all() serializer_class = TestRecordListSerializer ordering = ['-id'] @@ -59,7 +59,7 @@ class TestRecordViewSet(ModelViewSet): def perform_create(self, serializer): obj = serializer.save(create_by = self.request.user) # 如果检测合格 - if obj.fifo_detail: - obj.fifo_detail.is_testok = True if obj.is_testok else False - obj.fifo_detail.is_tested = True - obj.fifo_detail.save() \ No newline at end of file + if obj.fifo_item: + obj.fifo_item.is_testok = True if obj.is_testok else False + obj.fifo_item.is_tested = True + obj.fifo_item.save() \ No newline at end of file diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 035fa4a..3226671 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers from rest_framework.serializers import ModelSerializer -from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse +from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse from apps.inm.signals import update_inm from apps.mtm.models import Material, RecordForm, Step from apps.mtm.serializers import MaterialSimpleSerializer @@ -46,7 +46,7 @@ class PickSerializer(serializers.Serializer): i['fifo'] = fifo i['count'] = i.pop('pick_count') i['is_teskok'] = True - FIFODetail.objects.create(**i) + FIFOItem.objects.create(**i) # 更新车间物料 wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ subproduction_plan=sp,defaults={ From 0158998ef9ac5f4c1bb91e940382b1eddce61848 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 11:02:54 +0800 Subject: [PATCH 09/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qm/migrations/0005_auto_20211108_1102.py | 23 +++++++++++++++ hb_server/apps/qm/models.py | 4 +-- hb_server/apps/qm/serializers.py | 29 +++++++++++-------- hb_server/apps/qm/views.py | 17 ++++++++++- hb_server/apps/wpm/views.py | 2 +- 5 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 hb_server/apps/qm/migrations/0005_auto_20211108_1102.py diff --git a/hb_server/apps/qm/migrations/0005_auto_20211108_1102.py b/hb_server/apps/qm/migrations/0005_auto_20211108_1102.py new file mode 100644 index 0000000..428eae4 --- /dev/null +++ b/hb_server/apps/qm/migrations/0005_auto_20211108_1102.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.6 on 2021-11-08 03:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0004_auto_20211108_0951'), + ] + + operations = [ + migrations.RemoveField( + model_name='testrecord', + name='record_data', + ), + migrations.AlterField( + model_name='testrecorditem', + name='test_record', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_test_record', to='qm.testrecord', verbose_name='关联的检测记录'), + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index ac58211..8a3de4b 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -47,9 +47,7 @@ class TestRecord(CommonAModel): """ 检验记录 """ - form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE) - record_data = models.JSONField('记录数据', default=dict, blank=True) is_testok = models.BooleanField('是否合格', default=True) fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True) @@ -65,4 +63,4 @@ class TestRecordItem(BaseModel): field_value = models.JSONField('录入值', default=dict, blank=True) need_judge = models.BooleanField('是否需要判定', default=False) is_testok = models.BooleanField('是否合格', default=True) - test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE) \ No newline at end of file + test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE, related_name='item_test_record') \ No newline at end of file diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index 8924a14..628ba00 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -35,13 +35,18 @@ class AnalysisItemSerializer(serializers.ModelSerializer): pass -class TestRecordItemSerializer(serializers.ModelSerializer): +class TestRecordItemCreateSerializer(serializers.ModelSerializer): class Meta: model = TestRecordItem fields = ['form_field', 'field_value'] + +class TestRecordItemSerializer(serializers.ModelSerializer): + class Meta: + model = TestRecordItem + fields = '__all__' class TestRecordCreateSerializer(serializers.ModelSerializer): - record_data = TestRecordItemSerializer(many=True) + record_data = TestRecordItemCreateSerializer(many=True) class Meta: model = TestRecord fields = ['form', 'record_data', 'is_testok', 'fifo_item'] @@ -59,21 +64,21 @@ class TestRecordListSerializer(serializers.ModelSerializer): class TestRecordDetailSerializer(serializers.ModelSerializer): form_ = RecordFormSimpleSerializer(source='form', read_only=True) - record_data_ = serializers.SerializerMethodField() + record_data_ = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True) class Meta: model = TestRecord fields = '__all__' @staticmethod def setup_eager_loading(queryset): - queryset = queryset.select_related('form','fifo_detail') + queryset = queryset.select_related('form','fifo_item') return queryset - def get_record_data_(self, obj): - record_data = obj.record_data - all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort') - all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data - for i in all_fields_l: - key = i['field_key'] - i['field_value'] = record_data.get(key, None) - return all_fields_l \ No newline at end of file + # def get_record_data_(self, obj): + # record_data = obj.record_data + # all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort') + # all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data + # for i in all_fields_l: + # key = i['field_key'] + # i['field_value'] = record_data.get(key, None) + # return all_fields_l \ No newline at end of file diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index e43ec38..229eef6 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -1,5 +1,5 @@ from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer -from apps.qm.models import Standard, TestItem, TestRecord +from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem from django.shortcuts import render from rest_framework.viewsets import ModelViewSet from apps.system.mixins import CreateUpdateModelAMixin @@ -57,7 +57,22 @@ class TestRecordViewSet(ModelViewSet): return super().get_serializer_class() def perform_create(self, serializer): + vdata = serializer.validated_data obj = serializer.save(create_by = self.request.user) + tris = [] + for m in vdata['record_data']: # 保存记录详情 + form_field = m['form_field'] + m['field_name'] = form_field.field_name + m['field_key'] = form_field.field_key + m['field_type'] = form_field.field_type + m['field_value'] = m['field_value'] + m['sort'] = form_field.sort + m['need_judge'] = form_field.need_judge + m['is_testok'] = m['is_testok'] if 'is_testok' in m else True + m['test_record'] = obj + tris.append(TestRecordItem(**m)) + TestRecordItem.objects.bulk_create(tris) + # 如果检测合格 if obj.fifo_item: obj.fifo_item.is_testok = True if obj.is_testok else False diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 828ac89..ca82a2d 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -178,7 +178,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): m['field_name'] = form_field.field_name m['field_key'] = form_field.field_key m['field_type'] = form_field.field_type - m['field_value'] = form_field.field_value + m['field_value'] = m['field_value'] m['sort'] = form_field.sort m['wproduct_record'] = wr wrds.append(WProductRecordItem(**m)) From 2e9924f1d5b80bcb1d11d97ab72791e25b96d31c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 11:09:17 +0800 Subject: [PATCH 10/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/qm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index 628ba00..b91206b 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -46,7 +46,7 @@ class TestRecordItemSerializer(serializers.ModelSerializer): fields = '__all__' class TestRecordCreateSerializer(serializers.ModelSerializer): - record_data = TestRecordItemCreateSerializer(many=True) + record_data = TestRecordItemCreateSerializer(many=True, read_only=True) class Meta: model = TestRecord fields = ['form', 'record_data', 'is_testok', 'fifo_item'] From 3664971a17534466d919341da5c1ee456089d460 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 11:12:33 +0800 Subject: [PATCH 11/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/qm/serializers.py | 3 ++- hb_server/apps/qm/views.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index b91206b..02de10e 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -46,12 +46,13 @@ class TestRecordItemSerializer(serializers.ModelSerializer): fields = '__all__' class TestRecordCreateSerializer(serializers.ModelSerializer): - record_data = TestRecordItemCreateSerializer(many=True, read_only=True) + record_data = TestRecordItemCreateSerializer(many=True) class Meta: model = TestRecord fields = ['form', 'record_data', 'is_testok', 'fifo_item'] def create(self, validated_data): + validated_data.pop('record_data') if 'is_testok' not in validated_data: raise serializers.ValidationError('未填写检测结论') return super().create(validated_data) diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index 229eef6..6f2e725 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -57,10 +57,9 @@ class TestRecordViewSet(ModelViewSet): return super().get_serializer_class() def perform_create(self, serializer): - vdata = serializer.validated_data obj = serializer.save(create_by = self.request.user) tris = [] - for m in vdata['record_data']: # 保存记录详情 + for m in self.request.data['record_data']: # 保存记录详情 form_field = m['form_field'] m['field_name'] = form_field.field_name m['field_key'] = form_field.field_key From 229ab9b34a9b9ec6a7290cac7356091e28b28193 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 11:25:30 +0800 Subject: [PATCH 12/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/qm/serializers.py | 6 ------ hb_server/apps/qm/views.py | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index 02de10e..2ff328b 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -50,12 +50,6 @@ class TestRecordCreateSerializer(serializers.ModelSerializer): class Meta: model = TestRecord fields = ['form', 'record_data', 'is_testok', 'fifo_item'] - - def create(self, validated_data): - validated_data.pop('record_data') - if 'is_testok' not in validated_data: - raise serializers.ValidationError('未填写检测结论') - return super().create(validated_data) class TestRecordListSerializer(serializers.ModelSerializer): class Meta: diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index 6f2e725..0f29e9b 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -3,6 +3,9 @@ from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem from django.shortcuts import render from rest_framework.viewsets import ModelViewSet from apps.system.mixins import CreateUpdateModelAMixin +from rest_framework.exceptions import APIException +from rest_framework.response import Response +from rest_framework import status # Create your views here. class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet): """ @@ -56,10 +59,17 @@ class TestRecordViewSet(ModelViewSet): return TestRecordDetailSerializer return super().get_serializer_class() - def perform_create(self, serializer): + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + vdata = serializer.validated_data + record_data = vdata.pop('record_data') + if 'is_testok' not in vdata: + raise APIException('未填写检测结论') + obj = serializer.save(create_by = self.request.user) tris = [] - for m in self.request.data['record_data']: # 保存记录详情 + for m in record_data: # 保存记录详情 form_field = m['form_field'] m['field_name'] = form_field.field_name m['field_key'] = form_field.field_key @@ -76,4 +86,6 @@ class TestRecordViewSet(ModelViewSet): if obj.fifo_item: obj.fifo_item.is_testok = True if obj.is_testok else False obj.fifo_item.is_tested = True - obj.fifo_item.save() \ No newline at end of file + obj.fifo_item.save() + + return Response(status=status.HTTP_201_CREATED) \ No newline at end of file From 69fe69798835e91fd05ca3c07b59d119960abe5b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 11:28:29 +0800 Subject: [PATCH 13/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qm/migrations/0006_testrecorditem_sort.py | 18 ++++++++++++++++++ hb_server/apps/qm/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 hb_server/apps/qm/migrations/0006_testrecorditem_sort.py diff --git a/hb_server/apps/qm/migrations/0006_testrecorditem_sort.py b/hb_server/apps/qm/migrations/0006_testrecorditem_sort.py new file mode 100644 index 0000000..b57ff52 --- /dev/null +++ b/hb_server/apps/qm/migrations/0006_testrecorditem_sort.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 03:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0005_auto_20211108_1102'), + ] + + operations = [ + migrations.AddField( + model_name='testrecorditem', + name='sort', + field=models.IntegerField(default=1, verbose_name='排序号'), + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index 8a3de4b..f72b6a4 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -62,5 +62,6 @@ class TestRecordItem(BaseModel): field_type = models.IntegerField('字段类型', choices=RecordForm.type_choices) field_value = models.JSONField('录入值', default=dict, blank=True) need_judge = models.BooleanField('是否需要判定', default=False) + sort = models.IntegerField('排序号', default=1) is_testok = models.BooleanField('是否合格', default=True) test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE, related_name='item_test_record') \ No newline at end of file From 0bdd8a78a190ec2f6ac4f039b690a650fb22479b Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 13:06:01 +0800 Subject: [PATCH 14/23] =?UTF-8?q?=E4=BF=AE=E6=94=B9qm=E8=A1=A8=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0007_alter_testrecorditem_field_type.py | 18 ++++++++++++++++++ hb_server/apps/qm/models.py | 2 +- ...0007_alter_wproductrecorditem_field_type.py | 18 ++++++++++++++++++ hb_server/apps/wpm/models.py | 2 +- 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 hb_server/apps/qm/migrations/0007_alter_testrecorditem_field_type.py create mode 100644 hb_server/apps/wpm/migrations/0007_alter_wproductrecorditem_field_type.py diff --git a/hb_server/apps/qm/migrations/0007_alter_testrecorditem_field_type.py b/hb_server/apps/qm/migrations/0007_alter_testrecorditem_field_type.py new file mode 100644 index 0000000..70d454a --- /dev/null +++ b/hb_server/apps/qm/migrations/0007_alter_testrecorditem_field_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 05:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0006_testrecorditem_sort'), + ] + + operations = [ + migrations.AlterField( + model_name='testrecorditem', + name='field_type', + field=models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型'), + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index f72b6a4..0174d40 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -59,7 +59,7 @@ class TestRecordItem(BaseModel): form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False) field_name = models.CharField('字段名', max_length=50) field_key = models.CharField('字段标识', max_length=50) - field_type = models.IntegerField('字段类型', choices=RecordForm.type_choices) + field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50) field_value = models.JSONField('录入值', default=dict, blank=True) need_judge = models.BooleanField('是否需要判定', default=False) sort = models.IntegerField('排序号', default=1) diff --git a/hb_server/apps/wpm/migrations/0007_alter_wproductrecorditem_field_type.py b/hb_server/apps/wpm/migrations/0007_alter_wproductrecorditem_field_type.py new file mode 100644 index 0000000..47c78c2 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0007_alter_wproductrecorditem_field_type.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-08 05:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0006_rename_wproductrecorddetail_wproductrecorditem'), + ] + + operations = [ + migrations.AlterField( + model_name='wproductrecorditem', + name='field_type', + field=models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index a930c82..6325ae2 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -77,7 +77,7 @@ class WProductRecordItem(BaseModel): form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False) field_name = models.CharField('字段名', max_length=50) field_key = models.CharField('字段标识', max_length=50) - field_type = models.IntegerField('字段类型', choices=RecordForm.type_choices) + 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) From b083f1cd8d310f02e31826358a0ac3a727820595 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 8 Nov 2021 14:47:06 +0800 Subject: [PATCH 15/23] wpminit serializer --- hb_server/apps/wpm/models.py | 9 ++++++--- hb_server/apps/wpm/serializers.py | 21 +++++++++++++++++++++ hb_server/apps/wpm/views.py | 4 ++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 6325ae2..d1c80d8 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -23,10 +23,13 @@ class WProduct(CommonAModel): """ 半成品/成品 """ + WPR_ACT_STATE_DOING = 1 + WPR_ACT_STATE_TOTEST = 2 + WPR_ACT_STATE_OK = 3 act_state_choices=( - (1, '生产中'), - (2, '待检测'), - (3, '已合格') + (WPR_ACT_STATE_DOING, '生产中'), + (WPR_ACT_STATE_TOTEST, '待检测'), + (WPR_ACT_STATE_OK, '已合格') ) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 3226671..5cad10a 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -86,6 +86,27 @@ class WActionInitSerializer(serializers.Serializer): subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") wproducts = serializers.ListField(child= serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False) + + def validate(self, data): + subproduction_plan = data['subproduction_plan'] + step = data['step'] + + stepIds=[i['id'] for i in subproduction_plan.steps] + if step.id not in stepIds: + raise serializers.ValidationError('请选择正确的子工序操作') + + if 'wproducts' in data: + 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('请选择半成品进行操作') + return data + class DoInputSerializer(serializers.Serializer): id = serializers.PrimaryKeyRelatedField(queryset=WMaterial.objects.all(), label='车间物料ID') diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index ca82a2d..f5a2dd0 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -105,7 +105,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): 提交操作表单 """ data = request.data - serializer = WActionInitSerializer(data=data) + serializer = WActionSubmitSerializer(data=data, context={'request':self.request}) serializer.is_valid(raise_exception=True) vdata = serializer.validated_data #校验之后的数据 # 创建一个生产操作记录 @@ -140,7 +140,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): if ma.is_main: # 计划开始, 第一步切割创建动态产品 wpr = dict(m_state=ma, p_state=vdata['step'], - act_state=1, is_executed=True, remark='', + 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']): From 2b3f45eb7f847a8647a4e39b478d722ed9030a46 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 00:14:30 +0800 Subject: [PATCH 16/23] =?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 From 6a4c99f18c4b05529db9c5b41b43ae3ab05dc583 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 11:46:27 +0800 Subject: [PATCH 17/23] =?UTF-8?q?=E9=A2=86=E6=96=99bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/wpm/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 3609a70..509d3a4 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -45,7 +45,7 @@ class PickSerializer(serializers.Serializer): # 更新出库详情 i['fifo'] = fifo i['count'] = i.pop('pick_count') - i['is_teskok'] = True + i['is_testok'] = True # 默认检测合格 FIFOItem.objects.create(**i) # 更新车间物料 wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ From a28282e331f78f674878b19fd20599ce061190d1 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 13:20:07 +0800 Subject: [PATCH 18/23] wpm post bug --- hb_server/apps/wpm/serializers.py | 12 +++++++++++- hb_server/apps/wpm/urls.py | 3 ++- hb_server/apps/wpm/views.py | 16 ++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 509d3a4..4c4fd0a 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -3,7 +3,7 @@ from rest_framework.serializers import ModelSerializer from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse from apps.inm.signals import update_inm from apps.mtm.models import Material, RecordForm, Step -from apps.mtm.serializers import MaterialSimpleSerializer +from apps.mtm.serializers import MaterialSimpleSerializer, StepSimpleSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from django.utils import timezone @@ -80,6 +80,15 @@ class WMaterialListSerializer(serializers.ModelSerializer): model = WMaterial fields = '__all__' +class WProductListSerializer(serializers.ModelSerializer): + """ + 半成品列表 + """ + m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True) + p_state_ = StepSimpleSerializer(source='p_state', read_only=True) + class Meta: + model = WProduct + fields = '__all__' class OperationInitSerializer(serializers.Serializer): step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") @@ -136,6 +145,7 @@ class OperationSubmitSerializer(serializers.Serializer): input = DoInputSerializer(many=True, required=False) output = DoOutputSerializer(many=True, required=False) forms = OperationRecordSerializer(many=True, required=False) + remark = serializers.CharField(required=False, label='操作备注') \ No newline at end of file diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index 89604b5..89057f7 100644 --- a/hb_server/apps/wpm/urls.py +++ b/hb_server/apps/wpm/urls.py @@ -3,10 +3,11 @@ from rest_framework import urlpatterns from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.wpm.views import DoFormInit, DoFormSubmit, WMaterialViewSet, WPlanViewSet +from apps.wpm.views import DoFormInit, DoFormSubmit, WMaterialViewSet, WPlanViewSet, WProductViewSet router = DefaultRouter() router.register('wmaterial', WMaterialViewSet, basename='wmaterial') +router.register('wproduct', WProductViewSet, basename='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 270dc11..e3f7e27 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -14,7 +14,7 @@ from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem -from apps.wpm.serializers import PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer +from apps.wpm.serializers import PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer from rest_framework.response import Response # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): @@ -50,6 +50,18 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): serializer.save() return Response() +class WProductViewSet(ListModelMixin, GenericViewSet): + """ + 半成品 + """ + perms_map={'*':'*'} + queryset = WProduct.objects.select_related('p_state', 'm_state').all() + serializer_class = WProductListSerializer + filterset_fields = ['p_state', 'subproduction_plan', 'm_state', 'production_plan', 'p_state__process'] + search_fields = ['number'] + ordering_fields = ['id'] + ordering = ['id'] + class DoFormInit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} serializer_class=OperationInitSerializer @@ -114,7 +126,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): if 'wproducts' in data and data['wproducts']: action_obj.wproducts = data['wproducts'] action_obj.m_state = vdata[0].m_state - action_obj.remark = data['remark'] # 操作备注 + action_obj.remark = vdata.get('remark', '') # 操作备注 action_obj.create_by = request.user action_obj.save() From 48caae5c9a18bb6fe831a599422b184b156466fa Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 13:49:56 +0800 Subject: [PATCH 19/23] wpm post bug --- ...er_operationrecorditem_operation_record.py | 19 +++++++++++++++++++ hb_server/apps/wpm/models.py | 2 +- hb_server/apps/wpm/views.py | 5 ++--- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 hb_server/apps/wpm/migrations/0011_alter_operationrecorditem_operation_record.py diff --git a/hb_server/apps/wpm/migrations/0011_alter_operationrecorditem_operation_record.py b/hb_server/apps/wpm/migrations/0011_alter_operationrecorditem_operation_record.py new file mode 100644 index 0000000..daea66b --- /dev/null +++ b/hb_server/apps/wpm/migrations/0011_alter_operationrecorditem_operation_record.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.6 on 2021-11-09 05:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0010_auto_20211108_2335'), + ] + + operations = [ + migrations.AlterField( + model_name='operationrecorditem', + name='operation_record', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operationrecord', verbose_name='关联的生产记录'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 0aa63cc..1b3c8c3 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -82,5 +82,5 @@ class OperationRecordItem(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) - operation_record = models.ForeignKey(Operation, verbose_name='关联的生产记录', on_delete=models.CASCADE) + operation_record = models.ForeignKey(OperationRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index e3f7e27..4876301 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -149,8 +149,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): if 'output' in data and data['output']: for i in vdata['output']: # 已经序列化好的数据 ma = i['material'] - if ma.is_main: - # 计划开始, 第一步切割创建动态产品 + if vdata['subproduction_plan'].main_product == ma: # 如果是该计划主产物 # 如果是切割 # 获取下一步子工序 if vdata['step'].type == Step.STEP_TYPE_DIV: @@ -231,7 +230,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): m['sort'] = form_field.sort m['operation_record'] = wr wrds.append(OperationRecordItem(**m)) - Operation.objects.bulk_create(wrds) + OperationRecordItem.objects.bulk_create(wrds) return Response() \ No newline at end of file From 5beb5026ff72f70e3501115810975eaa38c9692c Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 15:37:50 +0800 Subject: [PATCH 20/23] wpm post bug --- hb_server/apps/wpm/serializers.py | 30 ++++++++++++++++++++++++++++-- hb_server/apps/wpm/urls.py | 3 ++- hb_server/apps/wpm/views.py | 26 +++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 4c4fd0a..b6ddf64 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,7 +8,8 @@ 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.wpm.models import WMaterial, WProduct, OperationRecord, OperationRecordItem +from apps.system.serializers import UserSimpleSerializer +from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -90,6 +91,27 @@ class WProductListSerializer(serializers.ModelSerializer): model = WProduct fields = '__all__' +class OperationDetailSerializer(serializers.ModelSerializer): + wproducts_ = serializers.SerializerMethodField() + create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True) + p_state_ = StepSimpleSerializer(source='p_state', 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) + m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True) + p_state_ = StepSimpleSerializer(source='p_state', read_only=True) + class Meta: + model = Operation + fields = '__all__' + + class OperationInitSerializer(serializers.Serializer): step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID") subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") @@ -105,10 +127,14 @@ class OperationInitSerializer(serializers.Serializer): raise serializers.ValidationError('请选择正确的子工序操作') if 'wproducts' in data and data['wproducts']: + if step.type == Step.STEP_TYPE_DIV: + raise serializers.ValidationError(_('不可进行此操作')) for i in data['wproducts']: + if i.is_executed: + raise serializers.ValidationError('不可进行操作') if i.subproduction_plan != subproduction_plan: raise serializers.ValidationError('半成品所属子计划不一致') - if i.step != step: + if i.p_state != step: raise serializers.ValidationError('半成品所属子工序不一致') else: if step.type != Step.STEP_TYPE_DIV: diff --git a/hb_server/apps/wpm/urls.py b/hb_server/apps/wpm/urls.py index 89057f7..7c43ac7 100644 --- a/hb_server/apps/wpm/urls.py +++ b/hb_server/apps/wpm/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.wpm.views import DoFormInit, DoFormSubmit, WMaterialViewSet, WPlanViewSet, WProductViewSet +from apps.wpm.views import DoFormInit, DoFormSubmit, OperationViewSet, 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('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 4876301..9cf608d 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -1,6 +1,6 @@ from django.shortcuts import render from rest_framework.generics import CreateAPIView, GenericAPIView -from rest_framework.mixins import ListModelMixin +from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.utils import serializer_helpers from rest_framework.utils.field_mapping import get_relation_kwargs from rest_framework.views import APIView @@ -14,7 +14,7 @@ from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem -from apps.wpm.serializers import PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer +from apps.wpm.serializers import OperationDetailSerializer, OperationListSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer from rest_framework.response import Response # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): @@ -55,13 +55,33 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 半成品 """ perms_map={'*':'*'} - queryset = WProduct.objects.select_related('p_state', 'm_state').all() + queryset = WProduct.objects.select_related('p_state', 'm_state').filter(is_hidden=True) serializer_class = WProductListSerializer filterset_fields = ['p_state', 'subproduction_plan', 'm_state', 'production_plan', 'p_state__process'] search_fields = ['number'] ordering_fields = ['id'] ordering = ['id'] + @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PickSerializer) + def test(): + pass + +class OperationViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): + """ + 生产操作记录 + """ + perms_map={'*':'*'} + queryset = Operation.objects.select_related('p_state', 'm_state').all() + serializer_class = OperationListSerializer + filterset_fields = ['p_state', 'm_state'] + ordering_fields = ['id'] + ordering = ['-id'] + + def get_serializer_class(self): + if self.action == 'retrieve': + return OperationDetailSerializer + return super().get_serializer_class() + class DoFormInit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} serializer_class=OperationInitSerializer From 0f229df8f0b267e245d79e15081c792a91a21df1 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 15:39:42 +0800 Subject: [PATCH 21/23] wpm post bug --- hb_server/apps/wpm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 9cf608d..e93cdb9 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -55,7 +55,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 半成品 """ perms_map={'*':'*'} - queryset = WProduct.objects.select_related('p_state', 'm_state').filter(is_hidden=True) + queryset = WProduct.objects.select_related('p_state', 'm_state').filter(is_hidden=False) serializer_class = WProductListSerializer filterset_fields = ['p_state', 'subproduction_plan', 'm_state', 'production_plan', 'p_state__process'] search_fields = ['number'] From 537fda2e867d5de90ce62de1f5bc1a72e5da4168 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 9 Nov 2021 16:18:34 +0800 Subject: [PATCH 22/23] wpm post bug --- hb_server/apps/wpm/serializers.py | 2 +- hb_server/apps/wpm/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index b6ddf64..ebe1931 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -101,7 +101,7 @@ class OperationDetailSerializer(serializers.ModelSerializer): fields = '__all__' def get_wproducts_(self, obj): - return list(WProduct.objects.filter(id__in=obj.wproducts_).values('id', 'number')) + 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) diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index e93cdb9..31fda3f 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -145,7 +145,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): action_obj.p_state = vdata['step'] if 'wproducts' in data and data['wproducts']: action_obj.wproducts = data['wproducts'] - action_obj.m_state = vdata[0].m_state + action_obj.m_state = vdata['wproducts'][0].m_state action_obj.remark = vdata.get('remark', '') # 操作备注 action_obj.create_by = request.user action_obj.save() From 5d5a1e76042054f2529c0c775f9e6a27049c6a19 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 10 Nov 2021 09:00:35 +0800 Subject: [PATCH 23/23] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hb_server/apps/inm/serializers.py | 44 +++++++++---------- hb_server/apps/inm/views.py | 8 ++-- hb_server/apps/qm/views.py | 41 +++++++++--------- hb_server/apps/wpm/serializers.py | 70 ++++++++++++++++--------------- hb_server/apps/wpm/views.py | 3 ++ 5 files changed, 88 insertions(+), 78 deletions(-) diff --git a/hb_server/apps/inm/serializers.py b/hb_server/apps/inm/serializers.py index 3bafb90..2f444d6 100644 --- a/hb_server/apps/inm/serializers.py +++ b/hb_server/apps/inm/serializers.py @@ -4,6 +4,7 @@ from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,I from apps.system.serializers import UserSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer +from django.db import transaction class WareHouseSerializer(serializers. ModelSerializer): create_by_=UserSimpleSerializer('create_by', read_only=True) @@ -91,28 +92,29 @@ class FIFOInPurSerializer(serializers.ModelSerializer): pass # 创建采购入库 - validated_data['type'] = FIFO.FIFO_TYPE_PUR_IN - obj = FIFO(**validated_data) - obj.save() - for i in details: - if 'details' in i: - p_details = i.pop('details') - if len(p_details) != i['count']: - raise serializers.ValidationError('数目对不上') + with transaction.atomic(): + validated_data['type'] = FIFO.FIFO_TYPE_PUR_IN + obj = FIFO(**validated_data) + obj.save() + for i in details: + if 'details' in i: + p_details = i.pop('details') + if len(p_details) != i['count']: + raise serializers.ValidationError('数目对不上') + else: + i['fifo'] = obj + fifod = FIFOItem.objects.create(**i) + p_list = [] + for x in p_details: + x['state'] = 1 + x['material'] = i['material'] + x['warehouse'] = validated_data['warehouse'] + x['batch'] = i['batch'] + x['fifos'] = [fifod.id] + p_list.append(IProduct(**x)) + IProduct.objects.bulk_create(p_list) else: i['fifo'] = obj - fifod = FIFOItem.objects.create(**i) - p_list = [] - for x in p_details: - x['state'] = 1 - x['material'] = i['material'] - x['warehouse'] = validated_data['warehouse'] - x['batch'] = i['batch'] - x['fifos'] = [fifod.id] - p_list.append(IProduct(**x)) - IProduct.objects.bulk_create(p_list) - else: - i['fifo'] = obj - FIFOItem.objects.create(**i) + FIFOItem.objects.create(**i) return obj diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index 032124a..4d88b8f 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -11,6 +11,7 @@ from apps.inm.signals import update_inm from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action from rest_framework.response import Response +from django.db import transaction # Create your views here. class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet): @@ -118,8 +119,9 @@ class FIFOViewSet(ListModelMixin, GenericViewSet): raise APIException('未检验通过, 不可审核') if obj.is_audited: raise APIException('该入库记录已审核通过') - obj.is_audited = True - obj.save() - update_inm(obj) # 更新库存 + with transaction.atomic(): + obj.is_audited = True + obj.save() + update_inm(obj) # 更新库存 return Response() \ No newline at end of file diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index 0f29e9b..cc59ab8 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -6,6 +6,7 @@ from apps.system.mixins import CreateUpdateModelAMixin from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework import status +from django.db import transaction # Create your views here. class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet): """ @@ -66,26 +67,26 @@ class TestRecordViewSet(ModelViewSet): record_data = vdata.pop('record_data') if 'is_testok' not in vdata: raise APIException('未填写检测结论') - - obj = serializer.save(create_by = self.request.user) - tris = [] - for m in record_data: # 保存记录详情 - form_field = m['form_field'] - m['field_name'] = form_field.field_name - m['field_key'] = form_field.field_key - m['field_type'] = form_field.field_type - m['field_value'] = m['field_value'] - m['sort'] = form_field.sort - m['need_judge'] = form_field.need_judge - m['is_testok'] = m['is_testok'] if 'is_testok' in m else True - m['test_record'] = obj - tris.append(TestRecordItem(**m)) - TestRecordItem.objects.bulk_create(tris) + with transaction.atomic(): + obj = serializer.save(create_by = self.request.user) + tris = [] + for m in record_data: # 保存记录详情 + form_field = m['form_field'] + m['field_name'] = form_field.field_name + m['field_key'] = form_field.field_key + m['field_type'] = form_field.field_type + m['field_value'] = m['field_value'] + m['sort'] = form_field.sort + m['need_judge'] = form_field.need_judge + m['is_testok'] = m['is_testok'] if 'is_testok' in m else True + m['test_record'] = obj + tris.append(TestRecordItem(**m)) + TestRecordItem.objects.bulk_create(tris) - # 如果检测合格 - if obj.fifo_item: - obj.fifo_item.is_testok = True if obj.is_testok else False - obj.fifo_item.is_tested = True - obj.fifo_item.save() + # 如果检测合格 + if obj.fifo_item: + obj.fifo_item.is_testok = True if obj.is_testok else False + obj.fifo_item.is_tested = True + obj.fifo_item.save() return Response(status=status.HTTP_201_CREATED) \ No newline at end of file diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index ebe1931..6d98c3d 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -10,6 +10,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from apps.system.serializers import UserSimpleSerializer from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem +from django.db import transaction class PickDetailSerializer(serializers.Serializer): material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") @@ -36,40 +37,41 @@ class PickSerializer(serializers.Serializer): except: raise serializers.ValidationError('物料不存在') # 创建出库记录 - operator = self.context['request'].user - validated_data['create_by'] = operator - validated_data['operator'] = operator - validated_data['type'] = FIFO.FIFO_TYPE_DO_OUT - validated_data['inout_date'] = timezone.now() - fifo = FIFO.objects.create(**validated_data) - for i in picks: - # 更新出库详情 - i['fifo'] = fifo - i['count'] = i.pop('pick_count') - i['is_testok'] = True # 默认检测合格 - FIFOItem.objects.create(**i) - # 更新车间物料 - wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ - subproduction_plan=sp,defaults={ - 'material':i['material'], - 'batch':i['batch'], - 'subproduction_plan':sp, - 'count':0 - }) - wm.count = wm.count + i['count'] - wm.save() - # 更新子计划物料情况 - spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1) - spp.count_pick = spp.count_pick + i['count'] - spp.save() - sp.is_picked=True - sp.state = 3 #生产中 - sp.state_date_real = timezone.now() #实际开工日期 - sp.save() - # 更新库存 - fifo.is_audited = True - fifo.save() - update_inm(fifo) + with transaction.atomic(): + operator = self.context['request'].user + validated_data['create_by'] = operator + validated_data['operator'] = operator + validated_data['type'] = FIFO.FIFO_TYPE_DO_OUT + validated_data['inout_date'] = timezone.now() + fifo = FIFO.objects.create(**validated_data) + for i in picks: + # 更新出库详情 + i['fifo'] = fifo + i['count'] = i.pop('pick_count') + i['is_testok'] = True # 默认检测合格 + FIFOItem.objects.create(**i) + # 更新车间物料 + wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ + subproduction_plan=sp,defaults={ + 'material':i['material'], + 'batch':i['batch'], + 'subproduction_plan':sp, + 'count':0 + }) + wm.count = wm.count + i['count'] + wm.save() + # 更新子计划物料情况 + spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1) + spp.count_pick = spp.count_pick + i['count'] + spp.save() + sp.is_picked=True + sp.state = 3 #生产中 + sp.state_date_real = timezone.now() #实际开工日期 + sp.save() + # 更新库存 + fifo.is_audited = True + fifo.save() + update_inm(fifo) return fifo class WMaterialListSerializer(serializers.ModelSerializer): diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 31fda3f..2cae722 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -16,6 +16,7 @@ from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, O from apps.wpm.serializers import OperationDetailSerializer, OperationListSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer from rest_framework.response import Response +from django.db import transaction # Create your views here. class WPlanViewSet(ListModelMixin, GenericViewSet): """ @@ -131,6 +132,8 @@ class DoFormInit(CreateAPIView, GenericAPIView): class DoFormSubmit(CreateAPIView, GenericAPIView): perms_map={'*':'*'} serializer_class = OperationSubmitSerializer + + @transaction.atomic def post(self, request, format=None): """ 提交操作表单