diff --git a/hb_server/apps/qm/migrations/0013_auto_20211202_1620.py b/hb_server/apps/qm/migrations/0013_auto_20211202_1620.py new file mode 100644 index 0000000..2909dc9 --- /dev/null +++ b/hb_server/apps/qm/migrations/0013_auto_20211202_1620.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2.9 on 2021-12-02 08:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0040_material_piece_count'), + ('qm', '0012_alter_testrecorditem_field_type'), + ] + + operations = [ + migrations.AddField( + model_name='testrecord', + name='step', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='关联的工序步骤'), + ), + migrations.AddField( + model_name='testrecord', + name='test_record', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='qm.testrecord', verbose_name='关联检验记录'), + ), + migrations.AddField( + model_name='testrecord', + name='type', + field=models.PositiveSmallIntegerField(choices=[(10, '子工序检验'), (20, '工序检验'), (30, '工序复检'), (40, '成品检验')], default=20), + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index c2dea88..eca8ed5 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -47,14 +47,27 @@ class TestRecord(CommonAModel): """ 检验记录 """ + TEST_STEP = 10 + TEST_PROCESS = 20 + TEST_PROCESS_RE = 30 + TEST_FINAL = 40 + type_choice = ( + (TEST_STEP, '子工序检验'), + (TEST_PROCESS, '工序检验'), + (TEST_PROCESS_RE, '工序复检'), + (TEST_FINAL, '成品检验') + ) form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE) + type = models.PositiveSmallIntegerField(choices=type_choice, default=TEST_PROCESS) is_testok = models.BooleanField('是否合格', default=True) is_testok_robot = models.BooleanField('自动判定的是否合格', default=True) number = models.CharField('产品编号', null=True, blank=True, max_length=50) wproduct = models.ForeignKey('wpm.wproduct', verbose_name='关联的动态产品', on_delete=models.CASCADE, null=True, blank=True) material = models.ForeignKey('mtm.material', verbose_name='关联的物料状态', on_delete=models.CASCADE, null=True, blank=True) + step = models.ForeignKey('mtm.step', verbose_name='关联的工序步骤', on_delete=models.CASCADE, null=True, blank=True) subproduction_plan = models.ForeignKey('pm.subproductionplan', 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) + test_record = models.ForeignKey('self', verbose_name='关联检验记录', on_delete=models.CASCADE, null=True, blank=True) remark = models.TextField('备注', default='') diff --git a/hb_server/apps/wpm/migrations/0028_auto_20211202_1620.py b/hb_server/apps/wpm/migrations/0028_auto_20211202_1620.py new file mode 100644 index 0000000..9772c46 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0028_auto_20211202_1620.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2021-12-02 08:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0027_pick_pickwproduct'), + ] + + operations = [ + migrations.AlterField( + model_name='wproduct', + name='act_state', + field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (30, '已合格'), (40, '库存中'), (50, '不合格'), (60, '待成品检验')], default=0, verbose_name='进行状态'), + ), + migrations.AlterField( + model_name='wproduct', + name='is_executed', + field=models.BooleanField(default=False, verbose_name='是否执行'), + ), + ] diff --git a/hb_server/apps/wpm/migrations/0029_auto_20211202_1630.py b/hb_server/apps/wpm/migrations/0029_auto_20211202_1630.py new file mode 100644 index 0000000..d6b195c --- /dev/null +++ b/hb_server/apps/wpm/migrations/0029_auto_20211202_1630.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2021-12-02 08:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wpm', '0028_auto_20211202_1620'), + ] + + operations = [ + migrations.RemoveField( + model_name='wproduct', + name='is_executed', + ), + migrations.AlterField( + model_name='wproduct', + name='operation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_operation', to='wpm.operation', verbose_name='关联操作'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index b1af088..b21c765 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -25,6 +25,7 @@ class WProduct(CommonAModel): 动态半成品/成品表 """ WPR_ACT_STATE_TORETEST = 6 + WPR_ACT_STATE_DOWAIT = 8 WPR_ACT_STATE_DOING = 10 WPR_ACT_STATE_TOTEST = 20 WPR_ACT_STATE_OK = 30 @@ -33,7 +34,8 @@ class WProduct(CommonAModel): WPR_ACT_STATE_TOFINALTEST = 60 act_state_choices=( (WPR_ACT_STATE_TORETEST, '待复检'), - (WPR_ACT_STATE_DOING, '生产中'), + (WPR_ACT_STATE_DOWAIT, '操作准备中'), + (WPR_ACT_STATE_DOING, '操作进行中'), (WPR_ACT_STATE_TOTEST, '待检验'), (WPR_ACT_STATE_OK, '已合格'), (WPR_ACT_STATE_INM, '库存中'), @@ -45,14 +47,13 @@ class WProduct(CommonAModel): pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step') step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='w_step') act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices) - is_executed = models.BooleanField('子工序是否已执行', default=False) is_hidden = models.BooleanField('是否隐藏', default=False) child = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE) remark = models.CharField('备注', max_length=200, null=True, blank=True) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan') warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True) operation = models.ForeignKey('wpm.operation', verbose_name='关联操作', - on_delete=models.SET_NULL, null=True, blank=True, related_name='current_operation') + on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation') class Pick(CommonADModel): diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 7bca29e..f7591de 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -100,7 +100,7 @@ class PickSerializer(serializers.Serializer): wids = IProduct.objects.filter(pk__in=[x.id for x in iproducts]).values_list('wproduct', flat=True) wproducts = WProduct.objects.filter(pk__in=wids) first_step = Step.objects.get(pk=sp.steps[0]['id']) - wproducts.update(step=first_step, is_executed=False, + wproducts.update(step=first_step, act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None, subproduction_plan=sp) sp.is_picked=True @@ -187,10 +187,10 @@ class OperationCreateSerializer(serializers.Serializer): if step.type == Step.STEP_TYPE_DIV: raise exceptions.APIException(_('不可进行此操作')) for i in data['wproducts']: - if i.act_state != WProduct.WPR_ACT_STATE_DOING: - raise exceptions.APIException('半成品不在生产状态') - if i.is_executed: - raise exceptions.APIException('不可进行操作') + if i.act_state != WProduct.WPR_ACT_STATE_DOWAIT: + raise exceptions.APIException('半成品不在待操作状态') + # if i.is_executed: + # raise exceptions.APIException('不可进行操作') # if i.subproduction_plan != subproduction_plan: # raise exceptions.APIException('半成品所属子计划不一致') if i.step != step: @@ -224,8 +224,8 @@ class OperationInitSerializer(serializers.Serializer): if step.type == Step.STEP_TYPE_DIV: raise exceptions.APIException(_('不可进行此操作')) for i in data['wproducts']: - if i.is_executed: - raise exceptions.APIException('不可进行操作') + if i.act_state != WProduct.WPR_ACT_STATE_DOWAIT: + raise exceptions.APIException('半成品不在待操作状态') # if i.subproduction_plan != subproduction_plan: # raise exceptions.APIException('半成品所属子计划不一致') if i.step != step: diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 5fc6d27..5687616 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -11,7 +11,7 @@ from apps.mtm.models import Material, RecordForm, RecordFormField, Step, Subprod from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer -from apps.qm.models import TestRecordItem +from apps.qm.models import TestRecord, TestRecordItem from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from rest_framework.decorators import action @@ -73,7 +73,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet): # raise exceptions.APIException('超过计划数') spp.save() wps = WProduct.objects.filter(pk__in=[x for x in i['wproducts']], act_state=WProduct.WPR_ACT_STATE_OK) - wps.update(step=first_step, is_executed=False, + wps.update(step=first_step, act_state=WProduct.WPR_ACT_STATE_TORETEST, is_hidden=False, warehouse=None, subproduction_plan=sp, update_by=request.user, update_time=timezone.now()) for i in wps: @@ -195,13 +195,19 @@ class WProductViewSet(ListModelMixin, GenericViewSet): vdata = serializer.validated_data record_data = vdata.pop('record_data') wproduct = vdata['wproduct'] - if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST, WProduct.WPR_ACT_STATE_TORETEST]: + if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST, + WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST]: raise exceptions.APIException('该产品当前状态不可检验') if 'is_testok' not in vdata: raise exceptions.APIException('未填写检测结论') - - obj = serializer.save(create_by = self.request.user, - material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan) + + savedict = dict(create_by = self.request.user, + material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan, step=wproduct.step) + if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: + savedict['type'] = TestRecord.TEST_PROCESS_RE + elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOFINALTEST: + savedict['type'] = TestRecord.TEST_FINAL + obj = serializer.save(**savedict) tris = [] for m in record_data: # 保存记录详情 form_field = m['form_field'] @@ -220,6 +226,10 @@ class WProductViewSet(ListModelMixin, GenericViewSet): if obj.is_testok: wproduct.act_state = WProduct.WPR_ACT_STATE_OK + if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: + wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT + elif wproduct.material == Material.MA_TYPE_GOOD: + wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST if wproduct.number is None: # 产生半成品编号 wproduct.number = 'WP'+ranstr(7) wproduct.save() @@ -235,47 +245,47 @@ class WProductViewSet(ListModelMixin, GenericViewSet): return Response() - @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer) - @transaction.atomic - def retest(self, request, pk=None): - """ - 复检 - """ - serializer = WpmTestRecordCreateSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - vdata = serializer.validated_data - record_data = vdata.pop('record_data') - wproduct = vdata['wproduct'] - if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST: - raise exceptions.APIException('该产品当前状态不可检验') - if 'is_testok' not in vdata: - raise exceptions.APIException('未填写检测结论') + # @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer) + # @transaction.atomic + # def retest(self, request, pk=None): + # """ + # 复检 + # """ + # serializer = WpmTestRecordCreateSerializer(data=request.data) + # serializer.is_valid(raise_exception=True) + # vdata = serializer.validated_data + # record_data = vdata.pop('record_data') + # wproduct = vdata['wproduct'] + # if wproduct.act_state != WProduct.WPR_ACT_STATE_TORETEST: + # raise exceptions.APIException('该产品当前状态不可检验') + # if 'is_testok' not in vdata: + # raise exceptions.APIException('未填写检测结论') - obj = serializer.save(create_by = self.request.user, - material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan) - 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 None - m['test_record'] = obj - tris.append(TestRecordItem(**m)) - TestRecordItem.objects.bulk_create(tris) + # obj = serializer.save(create_by = self.request.user, + # material=wproduct.material, number=wproduct.number, subproduction_plan=wproduct.subproduction_plan) + # 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 None + # m['test_record'] = obj + # tris.append(TestRecordItem(**m)) + # TestRecordItem.objects.bulk_create(tris) - # 如果检测合格, 变更动态产品进行状态 + # # 如果检测合格, 变更动态产品进行状态 - if obj.is_testok: - wproduct.act_state = WProduct.WPR_ACT_STATE_DOING - wproduct.save() - else:# 如果不合格 - wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK - wproduct.save() - return Response() + # if obj.is_testok: + # wproduct.act_state = WProduct.WProduct.WPR_ACT_STATE_DOWAIT + # wproduct.save() + # else:# 如果不合格 + # wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK + # wproduct.save() + # return Response() @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer) @transaction.atomic @@ -358,6 +368,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd if instance.is_submited: raise exceptions.APIException('该操作已提交') self.perform_destroy(instance) + # 恢复半成品可操作 + instance.wp_operation.all().update(act_state=WProduct.WPR_ACT_STATE_DOWAIT) return Response(status=status.HTTP_204_NO_CONTENT) @transaction.atomic @@ -376,7 +388,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 创建操作所用半成品关联记录 if 'wproducts' in vdata: owps = [] - WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']]).update(operation=op) + WProduct.objects.filter(pk__in=[x.id for x in vdata['wproducts']]).update(operation=op, act_state=WProduct.WPR_ACT_STATE_DOING) splans = WpmServies.get_subplans_queryset_from_wproducts(vdata['wproducts']) for wpd in vdata['wproducts']: owp = {} @@ -456,9 +468,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd wp.step = newstep wp.pre_step = step if hasNext: - wp.is_executed= False + wp.act_state= WProduct.WPR_ACT_STATE_DOWAIT else: - wp.is_executed = True wp.act_state = WProduct.WPR_ACT_STATE_TOTEST wp.material = wsp.main_product # 更新子计划进度 @@ -474,7 +485,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd if i.subproduction_progress.is_main: newstep, _ = WpmServies.get_next_step(i.subproduction_plan, step) wpr = dict(material=i.material, step=newstep, - act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='', + act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', subproduction_plan=i.subproduction_plan) for x in range(i.count): WProduct.objects.create(**wpr) @@ -486,11 +497,9 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd wproduct.step = newstep wproduct.subproduction_plan = i.subproduction_plan if hasNext: - wproduct.act_state = WProduct.WPR_ACT_STATE_DOING - wproduct.is_executed = False + wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT else: wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST - wproduct.is_executed = True # 更新子计划进度 instance = SubProductionProgress.objects.get(subproduction_plan=i.subproduction_plan, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) @@ -799,7 +808,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): if vdata['step'].type == Step.STEP_TYPE_DIV: newstep, _ = WpmServies.get_next_step(i['subproduction_plan'], vdata['step']) wpr = dict(material=ma, step=newstep, - act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='', + act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', subproduction_plan=i['subproduction_plan']) for x in range(i['count_output']): WProduct.objects.create(**wpr) @@ -831,11 +840,9 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): wproduct.subproduction_plan=vdata['subproduction_plan'] wproduct.parent = data['wproducts'] if hasNext: - wproduct.act_state=WProduct.WPR_ACT_STATE_DOING - wproduct.is_executed=False + wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT else: wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST - wproduct.is_executed=True wproduct.save() else: raise exceptions.APIException('请指定子计划') @@ -846,9 +853,8 @@ class DoFormSubmit(CreateAPIView, GenericAPIView): wproduct.step = newstep wproduct.pre_step=vdata['step'] if hasNext: - wproduct.is_executed= False + wproduct.act_state=WProduct.WPR_ACT_STATE_DOWAIT else: - wproduct.is_executed= True wproduct.act_state=WProduct.WPR_ACT_STATE_TOTEST wproduct.material=wproduct.subproduction_plan.main_product wproduct.save()