From 4538c328345fe6f09df19d5ef093b7c8301a81cf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 4 Jan 2022 16:43:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E6=96=99=E6=B8=85=E5=8D=95=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wpm/migrations/0044_auto_20220104_1641.py | 59 +++++++++++++++++++ hb_server/apps/wpm/models.py | 17 ++++-- hb_server/apps/wpm/serializers.py | 6 +- hb_server/apps/wpm/services.py | 39 ++++++------ hb_server/apps/wpm/views.py | 36 +++++++---- 5 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 hb_server/apps/wpm/migrations/0044_auto_20220104_1641.py diff --git a/hb_server/apps/wpm/migrations/0044_auto_20220104_1641.py b/hb_server/apps/wpm/migrations/0044_auto_20220104_1641.py new file mode 100644 index 0000000..d507775 --- /dev/null +++ b/hb_server/apps/wpm/migrations/0044_auto_20220104_1641.py @@ -0,0 +1,59 @@ +# Generated by Django 3.2.9 on 2022-01-04 08:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0042_alter_recordformfield_field_type'), + ('wpm', '0043_auto_20211231_1130'), + ] + + operations = [ + migrations.RemoveField( + model_name='operation', + name='use_scrap', + ), + migrations.AddField( + model_name='operationmaterial', + name='from_batch', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='源批次'), + ), + migrations.AddField( + model_name='operationmaterial', + name='from_material', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='om_fmaterial', to='mtm.material', verbose_name='源物料'), + ), + migrations.AddField( + model_name='operationmaterial', + name='use_scrap', + field=models.BooleanField(default=False, verbose_name='是否使用的边角料'), + ), + migrations.AddField( + model_name='operationwproduct', + name='ng_sign', + field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='当时的不合格标记'), + ), + migrations.AddField( + model_name='wproduct', + name='coperation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wp_coperation', to='wpm.operation', verbose_name='创建所关联操作'), + ), + migrations.AddField( + model_name='wproductflow', + name='coperation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wpf_coperation', to='wpm.operation', verbose_name='创建所关联操作'), + ), + migrations.AlterField( + model_name='operationmaterial', + name='material', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='om_material', to='mtm.material', verbose_name='可能产出的产品'), + ), + migrations.AlterField( + model_name='wproductflow', + name='operation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wpf_operation', to='wpm.operation', verbose_name='当前操作'), + ), + ] diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 1bac267..a01de96 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -96,6 +96,8 @@ class WProduct(CommonAModel): 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='wp_operation') + coperation = models.ForeignKey('wpm.operation', verbose_name='创建所关联操作', + on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_coperation') test = models.ForeignKey('qm.testrecord', verbose_name='当前检验', on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_test') ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', @@ -144,7 +146,9 @@ class WproductFlow(CommonAModel): 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) + on_delete=models.SET_NULL, null=True, blank=True, related_name='wpf_operation') + coperation = models.ForeignKey('wpm.operation', verbose_name='创建所关联操作', + on_delete=models.SET_NULL, null=True, blank=True, related_name='wpf_coperation') test = models.ForeignKey('qm.testrecord', verbose_name='当前检验', on_delete=models.SET_NULL, null=True, blank=True) ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', @@ -183,7 +187,6 @@ class Operation(CommonADModel): 生产操作 """ step = models.ForeignKey(Step, verbose_name='操作步骤', on_delete=models.CASCADE, null=True, blank=True) - use_scrap = models.BooleanField('是否使用的边角料', default=False) remark = models.CharField('操作备注', max_length=200, null=True, blank=True) is_submited = models.BooleanField('是否提交', default=False) @@ -196,7 +199,7 @@ class OperationWproduct(BaseModel): number = models.CharField('物品编号', null=True, blank=True, max_length=50) material = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='ow_subplan') - + ng_sign = models.PositiveSmallIntegerField('当时的不合格标记', choices= WProduct.ng_choices, null=True, blank=True) class Meta: unique_together = ( ('operation','wproduct') @@ -209,7 +212,8 @@ class OperationMaterial(BaseModel): type = models.IntegerField('类型', default=0, choices=SubprodctionMaterial.type_choices) operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE) - material = models.ForeignKey(Material, verbose_name='可能产出的产品', on_delete=models.CASCADE, null=True, blank=True) + material = models.ForeignKey(Material, verbose_name='可能产出的产品', on_delete=models.CASCADE, + null=True, blank=True, related_name='om_material') count = models.PositiveSmallIntegerField('消耗或产出数量', null=True, blank=True) wmaterial = models.ForeignKey(WMaterial, verbose_name='关联的车间物料', on_delete=models.CASCADE, null=True, blank=True) @@ -217,8 +221,12 @@ class OperationMaterial(BaseModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联的子计划', on_delete=models.CASCADE, null=True, blank=True) batch = models.CharField('批次号', max_length=100, null=True, blank=True) + use_scrap = models.BooleanField('是否使用的边角料', default=False) #以下为冷加工下料清单所用字段 + from_material = models.ForeignKey(Material, verbose_name='源物料', on_delete=models.CASCADE, + null=True, blank=True, related_name='om_fmaterial') + from_batch = models.CharField('源批次', max_length=100, null=True, blank=True) count_cut = models.PositiveIntegerField('切裁片数', default=0) count_real = models.PositiveIntegerField('生产片数', default=0) count_ok = models.PositiveIntegerField('成品数量', default=0) @@ -226,6 +234,7 @@ class OperationMaterial(BaseModel): count_podian = models.PositiveIntegerField('破点甩片', default=0) count_hua = models.PositiveIntegerField('划伤甩片', default=0) count_other = models.PositiveIntegerField('其他甩片', default=0) + class Meta: unique_together = ( ('operation','material', 'batch') diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index dcef7f8..b5d168c 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -350,7 +350,7 @@ class OperationMaterialCreate1Serailizer(serializers.ModelSerializer): wmaterial = serializers.PrimaryKeyRelatedField(required=True, queryset=WMaterial.objects.all()) class Meta: model = OperationMaterial - fields = ['operation', 'wmaterial', 'count'] + fields = ['operation', 'wmaterial', 'count', 'use_scrap'] def validate(self, attrs): if attrs['count'] <=0: @@ -376,7 +376,7 @@ class OperationMaterialCreate2Serailizer(serializers.ModelSerializer): subproduction_progress = serializers.PrimaryKeyRelatedField(required=True, queryset=SubProductionProgress.objects.all()) class Meta: model = OperationMaterial - fields = ['operation', 'subproduction_progress', 'count'] + fields = ['operation', 'subproduction_progress', 'count', 'use_scrap'] def create(self, validated_data): subproduction_progress = validated_data['subproduction_progress'] @@ -448,7 +448,7 @@ class WproductTicketListSerializer(serializers.ModelSerializer): class CuttingListSerializer(serializers.ModelSerializer): subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) - material_ = MaterialSimpleSerializer(source='material', read_only=True) + from_material_ = MaterialSimpleSerializer(source='from_material', read_only=True) create_by_ = UserSimpleSerializer(source='create_by', read_only=True) class Meta: model = OperationMaterial diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index d565482..76ab0fd 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -154,28 +154,23 @@ class WpmServies(object): """ inputs = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_IN) outputs = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - for i in inputs: - sp = i.subproduction_plan - i.count_cut = outputs.filter(subproduction_plan=sp).first().count - i.count_real = sp.count_real - i.count_ok = sp.count_ok - wpfs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True) - i.count_qipao = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_QIPAO).count() - i.count_podian = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_PODIAN).count() - i.count_hua = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_HUA).count() - i.count_other = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_OTHER).count() - i.is_cutting = True - i.save() - - @classmethod - def update_cutting_list_with_sp(cls, sp:SubProductionPlan): - """ - 根据子计划更新下料清单 - """ - wpfs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True) - for i in OperationMaterial.objects.filter(subproduction_plan=sp, is_cutting=True): - i.count_real = sp.count_real - i.count_ok = sp.count_ok + for i in outputs: + if i.use_scrap: # 如果使用了边角料 + i.count_cut = 0 + i.from_material = SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN, + subproduction__subplan_subprod=i.subproduction_plan).first().material + else: + input_q = inputs.filter(subproduction_plan=i.subproduction_plan) # 同计划的消耗 + i.from_material = input_q.first().matrial + count_cut = 0 + from_batch = '' + for m in input_q: + count_cut = count_cut + m.count + from_batch = from_batch + ';' + m.batch if m.batch else from_batch + wpfs = WproductFlow.objects.filter(subproduction_plan=i.subproduction_plan, + is_lastlog=True, coperation=op)# 筛选本次操作下子计划的产品日志 + i.count_real = wpfs.count() + i.count_ok = wpfs.exclude(scrap_reason=None).count() i.count_qipao = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_QIPAO).count() i.count_podian = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_PODIAN).count() i.count_hua = wpfs.filter(scrap_reason=WProduct.SCRAP_REASON_HUA).count() diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 2a47bca..464f0bd 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -386,7 +386,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet): obj.save() WpmServies.add_wproduct_flow_log(obj, 'scrap') if obj.step.process.id == 1: #如果是冷加工 - WpmServies.update_cutting_list_with_sp(obj.subproduction_plan) + WpmServies.update_cutting_list_with_operation(obj.coperation) return Response() # @action(methods=['get'], detail=False, perms_map={'get':'*'}) @@ -510,12 +510,13 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd owp['number'] = wpd.number owp['material'] = wpd.material owp['subproduction_plan'] = wpd.subproduction_plan + owp['ng_sign'] = wpd.ng_sign owps.append(OperationWproduct(**owp)) OperationWproduct.objects.bulk_create(owps) else: splans = WpmServies.get_subplans_queryset_from_wproducts(vdata['wproducts']) # 查询需要填写的自定义表格 - forms = RecordForm.objects.filter(step=step, type=RecordForm.RF_TYPE_DO) + forms = RecordForm.objects.filter(step=step, type=RecordForm.RF_TYPE_DO, enabled=True) for i in forms: opr = OperationRecord() opr.operation = op @@ -555,11 +556,22 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd step = op.step if op.is_submited: raise exceptions.APIException('该操作已提交') + # 校验消耗产出是否正确填写 + omis = OperationMaterial.objects.filter(operation=op, + type=SubprodctionMaterial.SUB_MA_TYPE_IN) + sps_omi_l = omis.values_list('subproduction_plan', flat=True) + omos = OperationMaterial.objects.filter(operation=op, + type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + sps_omo_l = omos.filter(use_scrap=False).values_list('subproduction_plan', flat=True) + if set(list(sps_omi_l)) == set(list(sps_omo_l)): + pass + else: + raise exceptions.APIException('消耗与产出不一致') # 检查自定义表单填写 if OperationRecord.objects.filter(operation=op, is_filled=False).exists(): raise exceptions.APIException('存在自定义表单未填写') # 更新物料消耗进度 - for i in OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_IN): + for i in omis: # 更新车间物料 i_wmat = i.wmaterial i_wmat.count = i_wmat.count- i.count @@ -570,7 +582,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd spp.count_real = spp.count_real + i.count spp.save() # 更新产出 - for i in OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT): + for i in omos: if not i.subproduction_progress.is_main: # 更新车间物料产出情况 ins, _ = WMaterial.objects.get_or_create(subproduction_plan=i.subproduction_plan, material=i.material) @@ -607,24 +619,23 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd elif step.type == Step.STEP_TYPE_DIV: # 更新物料产出情况 - outputs = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - if not outputs.exists(): + if not omos.exists(): raise exceptions.APIException('请选择物料产出') - for i in outputs: + for i in omos: 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_DOWAIT, remark='', - subproduction_plan=i.subproduction_plan, create_by=request.user) + subproduction_plan=i.subproduction_plan, + create_by=request.user, coperation=op) for x in range(i.count): ins = WProduct.objects.create(**wpr) # 添加日志 - WpmServies.add_wproduct_flow_log(ins, 'operation_submit') + WpmServies.add_wproduct_flow_log(ins, 'wproduct_create') # 更新进度 WpmServies.update_subproduction_progress_main(sp=i.subproduction_plan) elif step.type == Step.STEP_TYPE_COMB: - oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT, - subproduction_progress__is_main=True) + oms_w = omos.filter(subproduction_progress__is_main=True) if len(oms_w) == 1: oms_w = oms_w[0] # 校验单片数量是否正确, 暂时未写 @@ -640,8 +651,9 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 更新子计划进度 WpmServies.update_subproduction_progress_main(sp=oms_w.subproduction_plan) wproduct.create_by = request.user + wproduct.coperation = op wproduct.save() - WpmServies.add_wproduct_flow_log(wproduct, 'operation_submit') + WpmServies.add_wproduct_flow_log(wproduct, 'wproduct_create') # 隐藏原半成品 wps = WProduct.objects.filter(ow_wproduct__operation = op) wps.update(is_hidden=True, child=wproduct, update_by=request.user, update_time=timezone.now())