diff --git a/hb_server/apps/sam/views.py b/hb_server/apps/sam/views.py index 10a6d97..8a5f004 100644 --- a/hb_server/apps/sam/views.py +++ b/hb_server/apps/sam/views.py @@ -14,6 +14,8 @@ from rest_framework.decorators import action from django.db.models import F from rest_framework.response import Response from django.utils import timezone + +from apps.wf.models import Workflow # Create your views here. class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet): """ @@ -168,6 +170,10 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C FIFOItemProduct.objects.bulk_create(ipxs) # 更新成品库情况 ips.update(is_saled=True) + # 更新动态产品表情况 + from apps.wpm.models import WProduct + WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update( + act_state=WProduct.WPR_ACT_STATE_SELLED) # 更新库存 update_inm(fifo) # 变更审核状态 diff --git a/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py b/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py new file mode 100644 index 0000000..8013a5c --- /dev/null +++ b/hb_server/apps/wf/migrations/0021_auto_20211224_1426.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.9 on 2021-12-24 06:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0020_auto_20211223_1006'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='description', + field=models.CharField(blank=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述', max_length=100, null=True, verbose_name='描述'), + ), + migrations.AlterField( + model_name='customfield', + name='field_template', + field=models.TextField(blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder', null=True, verbose_name='文本域模板'), + ), + migrations.AlterField( + model_name='customfield', + name='field_type', + field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, cascader, cascaders,textarea, file', max_length=50, verbose_name='类型'), + ), + migrations.AlterField( + model_name='customfield', + name='placeholder', + field=models.CharField(blank=True, help_text='用户工单详情表单中作为字段的占位符显示', max_length=100, null=True, verbose_name='占位符'), + ), + ] diff --git a/hb_server/apps/wf/models.py b/hb_server/apps/wf/models.py index 35fc069..36a1cb3 100644 --- a/hb_server/apps/wf/models.py +++ b/hb_server/apps/wf/models.py @@ -174,8 +174,8 @@ class CustomField(CommonAModel): help_text='选项值,格式为list, 例["id":1, "name":"张三"]') label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用') - - is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的信息') + # hook = models.CharField('hook', max_length=1000, default='', help_text='获取下拉选项用于动态选项值') + is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的字段信息') class Ticket(CommonBModel): """ diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 169e295..55491ca 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -36,6 +36,7 @@ class WProduct(CommonAModel): WPR_ACT_STATE_NOTOK = 50 WPR_ACT_STATE_TOFINALTEST = 60 WPR_ACT_STATE_SCRAP = 70 + WPR_ACT_STATE_SELLED = 80 act_state_choices=( (WPR_ACT_STATE_TORETEST, '待复检'), (WPR_ACT_STATE_DOWAIT, '操作准备中'), @@ -46,7 +47,8 @@ class WProduct(CommonAModel): (WPR_ACT_STATE_INM, '已入库'), (WPR_ACT_STATE_NOTOK, '不合格'), (WPR_ACT_STATE_TOFINALTEST, '待成品检验'), - (WPR_ACT_STATE_SCRAP, '已报废') + (WPR_ACT_STATE_SCRAP, '已报废'), + (WPR_ACT_STATE_SELLED, '已售出'), ) SCRAP_REASON_QIPAO = 10 SCRAP_REASON_PODIAN = 20 @@ -58,6 +60,26 @@ class WProduct(CommonAModel): (30, '划伤'), (40, '其他') ) + + NG_BACK_WORK = 10 + NG_BACK_FIX = 20 + NG_SCRAP = 30 + NG_ACCEPT = 40 + NG_PERMIT = 50 + NG_DOWN = 60 + NG_BACK_FROM = 70 + NG_RECALL = 80 + + ng_choices = ( + (NG_BACK_WORK, '返工'), + (NG_BACK_FIX, '返修'), + (NG_SCRAP, '报废'), + (NG_ACCEPT, '让步接收'), + (NG_PERMIT, '偏离许可'), + (NG_DOWN, '降级使用'), + (NG_BACK_FROM, '退回供方'), + (NG_RECALL, '召回') + ) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step') @@ -69,6 +91,9 @@ class WProduct(CommonAModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan') scrap_reason = models.IntegerField('报废原因', choices=scrap_reason_choices, null=True, blank=True) + + ng_sign = models.PositiveSmallIntegerField('不合格标记', choices=ng_choices, null=True, blank=True) + 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') @@ -89,6 +114,7 @@ class WprouctTicket(CommonAModel): """ 玻璃审批工单 """ + number = models.CharField('物品编号', null=True, blank=True, max_length=50) wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE) @@ -96,7 +122,7 @@ class WprouctTicket(CommonAModel): subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE) ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket') - decision = models.CharField('最终决定', null=True, blank=True, max_length=100) + decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True) class Pick(CommonADModel): """ diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 9d56be0..30ba1f2 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -46,17 +46,18 @@ class WpmServies(object): if is_testok: if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检 wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT + wproduct.ng_sign = None # 把不合格标记去除 elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验 wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST else: wproduct.act_state = WProduct.WPR_ACT_STATE_OK if wproduct.number is None: # 产生半成品编号 wproduct.number = 'WP'+ranstr(7) - # 更新子计划状态 - # 更新子计划主产品数 + + # 更新子计划合格进度 instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题 + instance.count_ok = instance.count_ok + 1 instance.save() else:# 如果不合格 wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK diff --git a/hb_server/apps/wpm/signals.py b/hb_server/apps/wpm/signals.py index 2c816a2..3d27b38 100644 --- a/hb_server/apps/wpm/signals.py +++ b/hb_server/apps/wpm/signals.py @@ -1,9 +1,12 @@ from django.db.models.signals import post_save +from apps.mtm.models import SubprodctionMaterial +from apps.pm.models import SubProductionProgress from apps.qm.models import TestRecord from apps.wf.models import Ticket from django.dispatch import receiver - +from rest_framework import exceptions from apps.wpm.models import WProduct, WprouctTicket +from apps.wpm.models import OperationWproduct @receiver(post_save, sender=Ticket) @@ -43,18 +46,41 @@ def handleTicket(sender, instance, created, **kwargs): ticket_data = instance.ticket_data wt = instance.wt_ticket wp = wt.wproduct + decision = WProduct.NG_BACK_WORK + if 'decision_1' in ticket_data and ticket_data['decision_1']: - wt.decision = ticket_data['decision_1'] - if ticket_data['decision_1'] in ['返工', '返修']: - pass - elif ticket_data['decision_1'] in ['让步接收']: - wp.act_state = WProduct.WPR_ACT_STATE_OK + decision = ticket_data['decision_1'] elif 'decision_2' in ticket_data and ticket_data['decision_2']: - wp.decision = ticket_data['decision_2'] - if ticket_data['decision_2'] in ['返工', '返修']: - pass - elif ticket_data['decision_2'] in ['让步接收']: - wp.act_state = WProduct.WPR_ACT_STATE_OK - wt.save() - wp.save() + decision = ticket_data['decision_2'] + + + wp.ng_sign = decision + if decision in [WProduct.NG_BACK_WORK, WProduct.NG_BACK_FIX]: + step = ticket_data['back_step'] + wp.step = step + # 找到当时所属的计划 + sp = OperationWproduct.objects.filter(operation__is_submited=True, operation__step=step).first() + if sp: + wp.subproduction_plan = sp + wt.save() + wp.ticket = None # 解除当前工单 + wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT + wp.save() + # 更新子计划合格进度 + instance = SubProductionProgress.objects.get(subproduction_plan=sp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + instance.count_ok = instance.count_ok - 1 #进度计算这里该怎么处理呢 + instance.save() + + else: + raise exceptions.APIException('返回步骤点错误') + + elif decision in [WProduct.NG_ACCEPT, WProduct.NG_PERMIT]: + wp.act_state = WProduct.WPR_ACT_STATE_OK + wp.ng_sign = decision + wt.save() + wp.ticket = None # 解除当前工单 + wp.save() + + \ No newline at end of file diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index 8d906d2..ff06c97 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -370,8 +370,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet): 发起不合格审理单 """ obj = self.get_object() - if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK: - raise exceptions.APIException('非检验不合格产品不可发起不合格审理') + if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None: + raise exceptions.APIException('该产品不可发起不合格审理') workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first() if workflow: exist_data = { @@ -539,11 +539,13 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd else: wp.act_state = WProduct.WPR_ACT_STATE_TOTEST wp.material = wsp.main_product - # 更新子计划进度 - instance = SubProductionProgress.objects.get(subproduction_plan=wsp, - is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) - instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 - instance.save() + # 更新子计划生产进度 + # 如果产品有返工标记不做计算 + if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: + instance = SubProductionProgress.objects.get(subproduction_plan=wsp, + is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) + instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 + instance.save() wp.operation = None wp.update_by = request.user wp.save()