Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shijing 2021-12-28 17:07:22 +08:00
commit 95a9fa86dc
13 changed files with 209 additions and 30 deletions

View File

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class DevelopConfig(AppConfig):
name = 'apps.develop'
verbose_name = '开发调试接口'

View File

@ -0,0 +1,12 @@
from django.shortcuts import render
from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers, status
from rest_framework.response import Response
class CleanData(APIView):
pass

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0018_productionplan_process_json'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='process_json',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='按工序的统计数'),
),
]

View File

@ -11,7 +11,7 @@ from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep
from apps.pm.filters import SubproductionProgressFilterSet from apps.pm.filters import SubproductionProgressFilterSet
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render from django.shortcuts import render
@ -34,7 +34,7 @@ def updateOrderPlanedCount(order):
order.planed_count = planed_count order.planed_count = planed_count
order.save() order.save()
class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, GenericViewSet): class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, RetrieveModelMixin, GenericViewSet):
""" """
生产计划 生产计划
""" """

View File

@ -25,6 +25,7 @@ class WMaterialFilterSet(filters.FilterSet):
class WProductFilterSet(filters.FilterSet): class WProductFilterSet(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag') tag = filters.CharFilter(method='filter_tag')
production_plan = filters.NumberFilter(field_name='subproduction_plan__production_plan')
class Meta: class Meta:
model = WProduct model = WProduct
fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type'] fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']

View File

@ -0,0 +1,58 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
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 = [
('inm', '0024_auto_20211227_0948'),
('qm', '0022_auto_20211216_1401'),
('mtm', '0042_alter_recordformfield_field_type'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0019_alter_productionplan_process_json'),
('wf', '0023_auto_20211227_1318'),
('wpm', '0038_auto_20211227_0948'),
]
operations = [
migrations.AlterField(
model_name='wprouctticket',
name='step',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤/发现步骤'),
),
migrations.CreateModel(
name='WproductFlow',
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='删除标记')),
('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')),
('act_state', models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态')),
('is_hidden', models.BooleanField(default=False, verbose_name='是否隐藏')),
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='备注')),
('scrap_reason', models.IntegerField(blank=True, choices=[(10, '气泡'), (20, '破点'), (30, '划伤'), (40, '其他')], null=True, verbose_name='报废原因')),
('ng_sign', models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='不合格标记')),
('is_lastlog', models.BooleanField(default=True, verbose_name='是否该子计划下的最后一条日志')),
('child', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductflow')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')),
('operation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wpm.operation', verbose_name='当前操作')),
('pre_step', models.ForeignKey(blank=True, help_text='已执行完的步骤', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_pre_step', to='mtm.step', verbose_name='已执行到')),
('step', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_step', to='mtm.step', verbose_name='所在步骤')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='当前子生产计划')),
('test', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.testrecord', verbose_name='当前检验')),
('ticket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.ticket', verbose_name='当前工单')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('warehouse', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inm.warehouse', verbose_name='所在仓库')),
('wproduct', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='关联产品')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 07:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0039_auto_20211228_1417'),
]
operations = [
migrations.AlterField(
model_name='wproductflow',
name='number',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='物品编号'),
),
]

View File

@ -118,12 +118,40 @@ class WprouctTicket(CommonAModel):
number = models.CharField('物品编号', null=True, blank=True, max_length=50) number = models.CharField('物品编号', null=True, blank=True, max_length=50)
wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE) wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE) step = models.ForeignKey(Step, verbose_name='所在步骤/发现步骤', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE) 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') ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True) decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True)
class WproductFlow(CommonAModel):
"""
动态产品表日志
"""
wproduct = models.ForeignKey(WProduct, on_delete=models.CASCADE, verbose_name='关联产品', null=True, blank=True)
number = models.CharField('物品编号', 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='wl_pre_step')
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='wl_step')
act_state = models.IntegerField('进行状态', default=0, choices=WProduct.act_state_choices)
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)
scrap_reason = models.IntegerField('报废原因', choices= WProduct.scrap_reason_choices, null=True, blank=True)
ng_sign = models.PositiveSmallIntegerField('不合格标记', choices= WProduct.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)
test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
on_delete=models.SET_NULL, null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True)
is_lastlog = models.BooleanField('是否该子计划下的最后一条日志', default=True)
class Pick(CommonADModel): class Pick(CommonADModel):
""" """
领料记录 领料记录

View File

@ -5,7 +5,7 @@ from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.mtm.models import Material, Step, SubprodctionMaterial from apps.mtm.models import Material, Step, SubprodctionMaterial
from apps.qm.models import TestRecord from apps.qm.models import TestRecord
from apps.system.models import User from apps.system.models import User
from apps.wpm.models import WProduct from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from utils.tools import ranstr from utils.tools import ranstr
class WpmServies(object): class WpmServies(object):
@ -48,7 +48,7 @@ class WpmServies(object):
if is_testok: if is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检 if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT 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: # 成品检验 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 wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
else: else:
@ -56,14 +56,32 @@ class WpmServies(object):
if wproduct.number is None: # 产生半成品编号 if wproduct.number is None: # 产生半成品编号
wproduct.number = 'WP'+ranstr(7) wproduct.number = 'WP'+ranstr(7)
# 更新子计划合格进度 # 去除ng_sign
ins = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, if wproduct.ng_sign:
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) wt = WprouctTicket.objects.order_by('id').last() #取最后的工单
ins.count_ok = ins.count_ok + 1 if wt.step.process == test.step.process:
ins.save() wproduct.ng_sign = None
# 更新子计划相关进度
cls.update_subproduction_progress_main(sp=wproduct.subproduction_plan)
else:# 如果不合格 else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理的工单 # 需要走不合格品审理的工单
wproduct.update_by = user wproduct.update_by = user
wproduct.test = None wproduct.test = None
wproduct.save() wproduct.save()
@classmethod
def update_subproduction_progress_main(cls, sp:SubProductionPlan):
"""
根据产品变动日志更新生产进度
"""
objs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True)
count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).count()
count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST,
WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING])
ins = SubProductionProgress.objects.get(subproduction_plan=sp,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
ins.count_ok = count_ok
ins.count_real = count_real
ins.save()

View File

@ -6,8 +6,9 @@ from apps.qm.models import TestRecord
from apps.wf.models import Ticket from apps.wf.models import Ticket
from django.dispatch import receiver from django.dispatch import receiver
from rest_framework import exceptions from rest_framework import exceptions
from apps.wpm.models import WProduct, WprouctTicket from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from apps.wpm.models import OperationWproduct from apps.wpm.models import OperationWproduct
from apps.wpm.services import WpmServies
@receiver(post_save, sender=Ticket) @receiver(post_save, sender=Ticket)
@ -45,7 +46,7 @@ def handleTicket(sender, instance, created, **kwargs):
执行操作决定 执行操作决定
""" """
ticket_data = instance.ticket_data ticket_data = instance.ticket_data
wt = instance.wt_ticket wt = WprouctTicket.objects.get(ticket=instance)
wp = wt.wproduct wp = wt.wproduct
decision = WProduct.NG_BACK_WORK decision = WProduct.NG_BACK_WORK
@ -61,7 +62,7 @@ def handleTicket(sender, instance, created, **kwargs):
wp.step = step wp.step = step
# 找到当时所属的计划 # 找到当时所属的计划
sp = SubProductionPlan.objects.filter(ow_subplan__wproduct=wp, sp = SubProductionPlan.objects.filter(ow_subplan__wproduct=wp,
ow_subplan__operation__is_submited=True, ow_subplan__step=step).first() ow_subplan__operation__is_submited=True, ow_subplan__operation__step=step).first()
if sp: if sp:
wp.subproduction_plan = sp wp.subproduction_plan = sp
wt.save() wt.save()
@ -69,11 +70,7 @@ def handleTicket(sender, instance, created, **kwargs):
wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT
wp.save() wp.save()
# 更新子计划合格进度 # 更新子计划合格进度
if sp != wt.subproduction_plan: WpmServies.update_subproduction_progress_main(sp=sp)
ins = SubProductionProgress.objects.filter(subproduction_plan=sp,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
ins.count_ok = ins.count_ok - 1
ins.save()
else: else:
raise exceptions.APIException('返回步骤点错误') raise exceptions.APIException('返回步骤点错误')
@ -86,4 +83,15 @@ def handleTicket(sender, instance, created, **kwargs):
wp.save() wp.save()
@receiver(post_save, sender=WProduct)
def update_wproduct_log(sender, instance, created, **kwargs):
"""
更新产品变动日志
"""
WproductFlow.objects.filter(wproduct=instance, subproduction_plan=instance.subproduction_plan).update(is_lastlog=False)
ins = WproductFlow()
ins.wproduct = instance
for f in WproductFlow._meta.fields:
if f.name not in ['id', 'create_time', 'update_time', 'wproduct', 'is_lastlog']:
setattr(ins, f.name, getattr(instance, f.name, None))
ins.save()

View File

@ -180,14 +180,21 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
半成品 半成品
""" """
perms_map={'*':'*'} perms_map={'*':'*'}
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan').filter(is_hidden=False) queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan')
serializer_class = WProductListSerializer serializer_class = WProductListSerializer
filterset_class = WProductFilterSet filterset_class = WProductFilterSet
search_fields = ['number'] search_fields = ['number']
ordering_fields = ['id'] ordering_fields = ['id']
ordering = ['id'] ordering = ['id']
def get_queryset(self):
queryset = self.queryset
if self.request.query_params.get('tag', None) != 'show_hidden':
queryset = queryset.filter(is_hidden=False)
return queryset
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer) @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer)
@transaction.atomic
def test_init(self, request, pk=None): def test_init(self, request, pk=None):
""" """
检验记录创建及初始化 检验记录创建及初始化
@ -283,7 +290,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
FIFOItemProduct.objects.bulk_create(ips) FIFOItemProduct.objects.bulk_create(ips)
# 更新库存并修改半成品进行状态 # 更新库存并修改半成品进行状态
update_inm(fifo) update_inm(fifo)
wproducts.update(act_state=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now()) for i in wproducts:
i.act_state = WProduct.WPR_ACT_STATE_INM
i.warehouse = warehouse
i.update_by = request.user
i.update_time = timezone.now()
i.save()
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer)
@ -544,10 +556,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
# 更新子计划生产进度 # 更新子计划生产进度
# 如果产品有返工标记不做计算 # 如果产品有返工标记不做计算
if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]: if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]:
ins = SubProductionProgress.objects.get(subproduction_plan=wsp, WpmServies.update_subproduction_progress_main(sp=wsp)
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
ins.count_real = ins.count_real + 1
ins.save()
wp.operation = None wp.operation = None
wp.update_by = request.user wp.update_by = request.user
wp.save() wp.save()
@ -561,6 +570,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
subproduction_plan=i.subproduction_plan) subproduction_plan=i.subproduction_plan)
for x in range(i.count): for x in range(i.count):
WProduct.objects.create(**wpr) WProduct.objects.create(**wpr)
WpmServies.update_subproduction_progress_main(sp=i.subproduction_plan)
elif step.type == Step.STEP_TYPE_COMB: elif step.type == Step.STEP_TYPE_COMB:
oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT, oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT,
subproduction_progress__is_main=True) subproduction_progress__is_main=True)
@ -577,9 +587,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
else: else:
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
# 更新子计划进度 # 更新子计划进度
instance = oms_w.subproduction_progress WpmServies.update_subproduction_progress_main(sp=oms_w.subproduction_plan)
instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
instance.save()
wproduct.create_by = request.user wproduct.create_by = request.user
wproduct.save() wproduct.save()
# 隐藏原半成品 # 隐藏原半成品