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

This commit is contained in:
shijing 2022-01-20 15:45:38 +08:00
commit 9df7d39e05
31 changed files with 500 additions and 106 deletions

1
.gitignore vendored
View File

@ -4,4 +4,5 @@ deploy.sh
package-lock.json
.idea/
.vscode/
.idea/
server/static/

View File

@ -2,13 +2,14 @@ from django.db.models import base
from rest_framework import urlpatterns
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg
urlpatterns = [
path('cleandata/', CleanDataView.as_view()),
path('update_cutting/', UpdateCuttingView.as_view()),
path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_fifoitem/', UpdateFIFOItem.as_view())
path('update_fifoitem/', UpdateFIFOItem.as_view()),
path('update_spg/', UpdateSpg.as_view())
]

View File

@ -6,7 +6,7 @@ from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from apps.inm.models import FIFO, FIFOItem, Inventory, MaterialBatch
from apps.mtm.models import Material
from apps.pm.models import ProductionPlan
from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.sam.models import Order
from apps.wf.models import Ticket
from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow
@ -80,3 +80,14 @@ class UpdateFIFOItem(APIView):
i.is_testok = None
i.save()
return Response()
class UpdateSpg(APIView):
permission_classes = [IsAdminUser]
@transaction.atomic
def post(self, request, format=None):
"""
冷加工重新计算合格率
"""
for i in SubProductionPlan.objects.filter(subproduction__process__id=1):
WpmServies.update_subproduction_progress_main(sp=i)
return Response()

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('em', '0009_auto_20210916_1108'),
]
operations = [
migrations.AlterField(
model_name='equipment',
name='state',
field=models.PositiveIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='设备状态'),
),
]

View File

@ -0,0 +1,63 @@
# Generated by Django 3.2.9 on 2022-01-20 01:56
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),
('em', '0010_alter_equipment_state'),
]
operations = [
migrations.CreateModel(
name='ECheckRecord',
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='删除标记')),
('check_date', models.DateField(blank=True, null=True, verbose_name='校准检查日期')),
('description', 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='echeckrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'abstract': False,
},
),
migrations.RemoveField(
model_name='equipment',
name='belong_dept',
),
migrations.RemoveField(
model_name='equipment',
name='statedm',
),
migrations.AddField(
model_name='equipment',
name='next_check_date',
field=models.DateField(blank=True, null=True, verbose_name='下次校准检查日期'),
),
migrations.AlterField(
model_name='equipment',
name='state',
field=models.PositiveIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=0, verbose_name='设备状态'),
),
migrations.DeleteModel(
name='Equipmentrecord',
),
migrations.AddField(
model_name='echeckrecord',
name='equipment',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'),
),
migrations.AddField(
model_name='echeckrecord',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='echeckrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 3.2.9 on 2022-01-20 02:48
import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('em', '0011_auto_20220120_0956'),
]
operations = [
migrations.AddField(
model_name='equipment',
name='check_date',
field=models.DateField(blank=True, null=True, verbose_name='最近校准检查日期'),
),
migrations.AlterField(
model_name='echeckrecord',
name='check_date',
field=models.DateField(default=datetime.datetime(2022, 1, 20, 2, 48, 20, 706844, tzinfo=utc), verbose_name='校准检查日期'),
preserve_default=False,
),
migrations.AlterField(
model_name='echeckrecord',
name='equipment',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'),
preserve_default=False,
),
migrations.AlterField(
model_name='equipment',
name='cycle',
field=models.IntegerField(blank=True, null=True, verbose_name='校准或检定周期(月)'),
),
migrations.AlterField(
model_name='equipment',
name='next_check_date',
field=models.DateField(blank=True, null=True, verbose_name='预计下次校准检查日期'),
),
]

View File

@ -3,32 +3,34 @@ from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
#from apps.mtm.models import Process
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords
class Equipment(CommonBModel):
class Equipment(CommonAModel):
"""
设备台账信息
"""
EQUIP_STATE_OK = 10
EQUIP_STATE_LIMIT = 20
EQUIP_STATE_FIX = 30
EQUIP_STATE_DISABLE = 40
state_choices = (
(0, '完好'),
(1, '限用'),
(2, '在修'),
(3, '禁用')
)
statedm_choices = (
(0, '合格'),
(1, '准用'),
(2, '限用'),
(3, '禁用'),
(4, '停用'),
(5, '封存')
)
(EQUIP_STATE_OK, '完好'),
(EQUIP_STATE_LIMIT, '限用'),
(EQUIP_STATE_FIX, '在修'),
(EQUIP_STATE_DISABLE, '禁用')
)
state2_choices = (
(EQUIP_STATE_OK, '合格'),
(EQUIP_STATE_DISABLE, '禁用')
)
EQUIP_TYPE_PRO = 1
EQUIP_TYPE_TEST = 2
type_choices = (
(1, '生产设备'),
(2, '检验工具')
@ -54,20 +56,21 @@ class Equipment(CommonBModel):
factory = models.CharField('生产厂', max_length=50, null=True, blank=True)
production_date = models.DateField('生产日期', null=True, blank=True)
buy_date = models.DateField('购置日期', null=True, blank=True)
state = models.CharField('设备状态', max_length=11, choices=state_choices, default=0)
state = models.PositiveIntegerField('设备状态', choices=state_choices, default=0)
parameter = models.TextField('技术参数', null=True, blank=True)
place = models.CharField('存放位置', max_length=50, null=True, blank=True)
count = models.IntegerField('数量', default=0)
keeper = models.ForeignKey(User, verbose_name='保管人', on_delete=models.CASCADE, null=True, blank=True)
description = models.CharField('描述', max_length=200, blank=True, null=True)
#process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE, null=True, blank=True)
mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)#监视,测量设备
way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)#监视,测量设备
standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)#监视,测量设备
cycle = models.IntegerField('校准或检定周期', default=0)#监视,测量设备
usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)#监视,测量设备
statedm = models.IntegerField('设备状态', choices=statedm_choices, default=0)#监视,测量设备
# 以下是监视测量设备单独字段
mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)
way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)
standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)
cycle = models.IntegerField('校准或检定周期(月)', null=True, blank=True)
usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)
check_date = models.DateField('最近校准检查日期', blank=True, null=True)
next_check_date = models.DateField('预计下次校准检查日期',blank=True, null=True)
class Meta:
verbose_name = '设备信息'
verbose_name_plural = verbose_name
@ -75,8 +78,10 @@ class Equipment(CommonBModel):
def __str__(self):
return self.number + '-' + self.name
class Equipmentrecord(CommonBModel):
equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE, null=True, blank=True)
recentlydate = models.DateField('最近一次校准/检定日期',blank=True, null=True)
nextdate = models.DateField('下次应校准或检定日期',blank=True, null=True)
class ECheckRecord(CommonADModel):
"""
校准鉴定记录
"""
equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE)
check_date = models.DateField('校准检查日期')
description = models.CharField('描述', max_length=200, blank=True, null=True)

View File

@ -1,13 +1,12 @@
from apps.mtm.models import Step
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from .models import Equipment,Equipmentrecord
from rest_framework import exceptions
from .models import Equipment, ECheckRecord
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
class EquipmentSerializer(ModelSerializer):
belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True)
class EquipmentListSerializer(ModelSerializer):
keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField()
class Meta:
@ -17,24 +16,35 @@ class EquipmentSerializer(ModelSerializer):
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('belong_dept','keeper')
queryset = queryset.select_related('keeper')
return queryset
def get_step_(self, obj):
return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
class EquipmentCreateUpdateSerializer(ModelSerializer):
class Meta:
model = Equipment
exclude = ['create_by', 'update_by', 'create_time', 'update_time', 'check_date', 'next_check_date']
def validate(self, attrs):
if attrs['type'] == Equipment.EQUIP_TYPE_TEST:
if attrs['state'] not in [Equipment.EQUIP_STATE_OK, Equipment.EQUIP_STATE_DISABLE]:
raise exceptions.APIException('设备状态错误')
return super().validate(attrs)
class EquipmentSimpleSerializer(ModelSerializer):
class Meta:
model = Equipment
fields = ['id', 'number', 'name', 'state']
fields = ['id', 'number', 'name', 'state', 'model']
class EquipmentrecordSerializer(ModelSerializer):
equipment_ = EquipmentSerializer(source='equipment', read_only=True)
class ECheckRecordListSerializer(ModelSerializer):
equipment_ = EquipmentSimpleSerializer(source='equipment', read_only=True)
class Meta:
model = Equipmentrecord
model = ECheckRecord
fields = '__all__'
@staticmethod
@ -43,6 +53,11 @@ class EquipmentrecordSerializer(ModelSerializer):
queryset = queryset.select_related('equipment')
return queryset
class EChcekRecordCreateSerializer(ModelSerializer):
class Meta:
model = ECheckRecord
fields = ['equipment', 'check_date', 'description']
class DaqCreateSerializer(serializers.Serializer):
number = serializers.CharField()
file = serializers.FileField()

View File

@ -0,0 +1,20 @@
from tabnanny import check
from apps.em.models import ECheckRecord, Equipment
from dateutil.relativedelta import relativedelta
from django.utils import timezone
class EmService:
@classmethod
def update_check_date(cls, equip:Equipment):
# 根据校准检定记录变更下次日期
check = ECheckRecord.objects.filter(equipment=equip).order_by('id').last()
if check:
equip.check_date = check.check_date
if equip.cycle:
equip.next_check_date = equip.check_date + relativedelta(months=equip.cycle)
else:
equip.next_check_date = None
else:
equip.check_date = None
equip.next_check_date = None
equip.save()

View File

@ -1,12 +1,12 @@
from django.db.models import base
from rest_framework import urlpatterns
from apps.em.views import DaqView, EquipmentViewSet,EquipmentrecordViewSet
from apps.em.views import DaqView, EquipmentViewSet, EChcekRecordViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('equipment', EquipmentViewSet, basename='equipment')
router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord')
router.register('echeck_record', EChcekRecordViewSet, basename='echeck_record')
urlpatterns = [
path('daq/', DaqView.as_view()),
path('', include(router.urls)),

View File

@ -1,14 +1,20 @@
from datetime import timedelta
from django.shortcuts import render
from numpy import delete
from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin
from rest_framework import serializers, status
from rest_framework.response import Response
from apps.em.models import Equipment,Equipmentrecord
from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer
from apps.em.models import Equipment, ECheckRecord
from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \
EquipmentCreateUpdateSerializer, EquipmentListSerializer
from apps.em.services import EmService
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from django.utils import timezone
from django.db import transaction
# Create your views here.
class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet):
@ -18,48 +24,48 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet)
perms_map = {'get': '*', 'post': 'equipment_create',
'put': 'equipment_update', 'delete': 'equipment_delete'}
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
serializer_class = EquipmentListSerializer
search_fields = ['number', 'name','description']
filterset_fields = ['belong_dept', 'keeper', 'type']
filterset_fields = ['keeper', 'type']
ordering_fields = ['create_time']
ordering = ['-create_time']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return EquipmentCreateUpdateSerializer
return super().get_serializer_class()
# Create your views here.
class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet):
class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin,
CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet):
"""
设备校准-增删改查
"""
perms_map = {'get': '*', 'post': 'equipmentrecord_create',
'put': 'equipmentrecord_update', 'delete': 'equipmentrecord_delete'}
queryset = Equipmentrecord.objects.all()
serializer_class = EquipmentrecordSerializer
perms_map = {'get': '*', 'post': 'echeckrecord_create', 'delete': 'echeckrecord_delete'}
queryset = ECheckRecord.objects.all()
serializer_class = ECheckRecordListSerializer
filterset_fields = ['equipment']
ordering_fields = ['create_time']
ordering = ['-create_time']
ordering = ['-id']
def get_serializer_class(self):
if self.action in ['create']:
return EChcekRecordCreateSerializer
return super().get_serializer_class()
@transaction.atomic
def create(self, request, *args, **kwargs):
data = request.data
if data.get('equipment', None):
equipment = Equipment.objects.get(pk=data['equipment'])
equipment.statedm = data['state']
equipment.save()
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_200_OK)
def update(self, request, *args, **kwargs):
data = request.data
if data.get('equipment', None):
equipment = Equipment.objects.get(pk=data['equipment'])
equipment.statedm = data['state']
equipment.save()
id = self.get_object()
serializer = self.get_serializer(id, data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_200_OK)
instance = serializer.save(create_by=request.user)
EmService.update_check_date(equip=instance.equipment)
return Response()
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.delete()
EmService.update_check_date(equip=instance.equipment)
return Response()
import uuid
import os

View File

@ -36,8 +36,8 @@ class Employee(CommonAModel):
def __str__(self):
return self.name
class Attendance(CommonADModel):
"""
出勤记录
"""
# class Attendance(CommonADModel):
# """
# 出勤记录
# """

View File

@ -21,7 +21,8 @@ class MbFilterSet(filters.FilterSet):
class IProductFilterSet(filters.FilterSet):
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte')
update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte')
class Meta:
model = IProduct
fields = ['material', 'warehouse', 'batch', 'order', 'material__type']
fields = ['material', 'warehouse', 'batch', 'order', 'material__type', 'is_saled', 'update_time_start', 'update_time_end']

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-19 07:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0043_auto_20220106_0942'),
]
operations = [
migrations.AddField(
model_name='subproduction',
name='need_combtest',
field=models.BooleanField(default=False, verbose_name='需要质检'),
),
]

View File

@ -210,6 +210,7 @@ class SubProduction(CommonAModel):
name = models.CharField('命名', max_length=50, null=True, blank=True)
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process')
need_combtest = models.BooleanField('需要质检', default=False)
sort = models.IntegerField('排序号', default=1)
class Meta:

View File

@ -72,6 +72,11 @@ class SubProductionSerializer(serializers.ModelSerializer):
model = SubProduction
fields = '__all__'
class SubProductionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = SubProduction
fields = ['name', 'product', 'process', 'need_combtest', 'sort']
class OtherMaterialSerializer(serializers.ModelSerializer):
class Meta:
model = SubprodctionMaterial

View File

@ -4,7 +4,7 @@ from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelM
from apps.mtm.filters import MaterialFilterSet, TechDocFilterset
from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionCreateUpdateSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action
from rest_framework.response import Response
@ -80,6 +80,10 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
search_fields = ['name']
serializer_class = SubProductionSerializer
ordering = ['sort']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return SubProductionCreateUpdateSerializer
return super().get_serializer_class()
class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
"""

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sam', '0010_auto_20211208_1408'),
('pm', '0022_auto_20211229_1429'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_order', to='sam.order', verbose_name='关联订单'),
),
]

View File

@ -4,7 +4,7 @@ from django.db import transaction
from rest_framework import serializers
from rest_framework.views import APIView
from apps.em.models import Equipment
from apps.em.serializers import EquipmentSerializer
from apps.em.serializers import EquipmentSimpleSerializer
from apps.inm.models import MaterialBatch
from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep
@ -248,5 +248,5 @@ class ResourceViewSet(GenericViewSet):
subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False)
steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions)
equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct()
serializer = EquipmentSerializer(instance=equips, many=True)
serializer = EquipmentSimpleSerializer(instance=equips, many=True)
return Response(serializer.data)

View File

@ -20,3 +20,7 @@ class PlanGanttSerializer(serializers.ModelSerializer):
def get_children(self, obj):
subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number')
return SubplanGanttSerializer(instance=subplans, many=True).data
class ProcessYieldSerializer(serializers.Serializer):
datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True)
datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True)

View File

@ -3,11 +3,12 @@ from rest_framework import urlpatterns
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.srm.views import GanttPlan
from apps.srm.views import GanttPlan, ProcessYieldView
router = DefaultRouter()
urlpatterns = [
path('gantt/plan/', GanttPlan.as_view()),
path('process/yield/', ProcessYieldView.as_view()),
path('', include(router.urls)),
]

View File

@ -1,9 +1,14 @@
from django.shortcuts import render
from rest_framework import serializers
from rest_framework.generics import ListAPIView
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.views import APIView
from rest_framework.response import Response
from apps.mtm.models import Process, Step
from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.srm.serializers import PlanGanttSerializer
from apps.srm.serializers import PlanGanttSerializer, ProcessYieldSerializer
from apps.wpm.models import WProduct, WproductFlow
from django.db.models import Count
# Create your views here.
class GanttPlan(ListAPIView):
@ -15,4 +20,48 @@ class GanttPlan(ListAPIView):
queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process')
ordering = ['-id']
class ProcessYieldView(CreateAPIView):
"""
工序成品率统计
"""
perms_map = {'get':'*'}
serializer_class = ProcessYieldSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wpfs = WproductFlow.objects.filter(is_lastlog=True)
if vdata.get('datetime_start', None):
wpfs = wpfs.filter(update_time__gte = vdata.get('datetime_start'))
if vdata.get('datetime_end', None):
wpfs = wpfs.filter(update_time__lte = vdata.get('datetime_end'))
# 根据产品日志记录进行聚合
count_ok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).values('step__process__id').annotate(count_ok=Count('id')))
count_notok_g = list(
(
wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1)
| wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP],
step__process__id=1).exclude(number=None)
)\
.values('step__process__id',
).annotate(count_notok=Count('id')))
ret = []
process_l = list(Process.objects.filter(is_deleted=False).order_by('number').values('id', 'name'))
for i in process_l:
ret_item = {'id':i['id'], 'name':i['name'], 'count_ok':0, 'count_notok':0, 'rate':1}
for m in count_ok_g:
if m['step__process__id'] == ret_item['id']:
ret_item['count_ok'] = m['count_ok']
for n in count_notok_g:
if n['step__process__id'] == ret_item['id']:
ret_item['count_notok'] = n['count_notok']
rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \
if ret_item['count_ok']+ret_item['count_notok']>0 else 1
ret_item['rate'] = rate
ret.append(ret_item)
return Response(ret)

View File

@ -41,6 +41,12 @@ class WProductFilterSet(filters.FilterSet):
def filter_tag(self, queryset, name, value):
if value == 'no_scrap':
queryset = queryset.exclude(act_state=WProduct.WPR_ACT_STATE_SCRAP)
elif value == 'notok':
queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP])\
.exclude(step__process__id = 1) # 不算冷加工的报废
elif value == 'ok':
queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED])
return queryset

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0048_operationwproduct_place'),
]
operations = [
migrations.AddField(
model_name='operationequip',
name='state',
field=models.PositiveSmallIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='当前设备状态'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-20 02:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0049_operationequip_state'),
]
operations = [
migrations.AlterField(
model_name='operationequip',
name='state',
field=models.PositiveSmallIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=10, verbose_name='当前设备状态'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2022-01-20 07:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sam', '0010_auto_20211208_1408'),
('wpm', '0050_alter_operationequip_state'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='to_order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'),
),
migrations.AddField(
model_name='wproductflow',
name='to_order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'),
),
]

View File

@ -119,6 +119,7 @@ class WProduct(CommonAModel):
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -192,7 +193,7 @@ class WproductFlow(CommonAModel):
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)
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -336,4 +337,5 @@ class OperationEquip(BaseModel):
Operation, verbose_name='关联操作', on_delete=models.CASCADE, related_name='oe_operation')
equip = models.ForeignKey(Equipment, verbose_name='生产设备',
on_delete=models.CASCADE, related_name='oe_equip')
state = models.PositiveSmallIntegerField('当前设备状态', choices=Equipment.state_choices, default=Equipment.EQUIP_STATE_OK)
remark = models.TextField('备注', null=True, blank=True)

View File

@ -19,6 +19,7 @@ from apps.system.models import User
from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket
from django.db import transaction
from apps.sam.models import Order
class PickHalfSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID')
@ -155,6 +156,7 @@ class WProductListSerializer(serializers.ModelSerializer):
subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
children = serializers.SerializerMethodField()
to_order_ = OrderSimpleSerializer(source='to_order', read_only=True)
class Meta:
model = WProduct
fields = '__all__'
@ -561,3 +563,7 @@ class WproductMtestSerializer(serializers.ModelSerializer):
class Meta:
model = WProduct
fields = ['remark_mtest', 'is_mtestok']
class WproductToOrderSerializer(serializers.Serializer):
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True)
order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all())

View File

@ -54,7 +54,8 @@ class WpmServies(object):
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
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.step.type == Step.STEP_TYPE_COMB: # 夹层检验
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and \
wproduct.subproduction_plan.subproduction.need_combtest : # 配置中需要质检
wproduct.act_state = WProduct.WPR_ACT_STATE_TOCOMBTEST
else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK
@ -113,7 +114,11 @@ class WpmServies(object):
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_notok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).count()
count_notok = (
objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1)
| objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP],
step__process__id=1).exclude(number=None)
).count()
count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST,
WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count()
ins = SubProductionProgress.objects.filter(subproduction_plan=sp,

View File

@ -16,7 +16,7 @@ from rest_framework.decorators import action
from apps.wf.models import Workflow
from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, \
OperationMaterial, OperationRecord, OperationRecordItem, WprouctTicket
OperationMaterial, OperationRecord, OperationRecordItem, WproductFlow, WprouctTicket
from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerializer, OperationEquipUpdateSerializer, \
OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \
@ -27,7 +27,7 @@ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerial
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \
WproductPutInsSerializer, WproductTicketListSerializer
WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
from rest_framework.response import Response
from django.db import transaction
@ -148,7 +148,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
perms_map = {'*': '*'}
queryset = WProduct.objects.select_related('step', 'material',
'subproduction_plan', 'warehouse').prefetch_related('wproduct_child')
'subproduction_plan', 'warehouse', 'order').prefetch_related('wproduct_child')
serializer_class = WProductListSerializer
filterset_class = WProductFilterSet
search_fields = ['number']
@ -441,6 +441,30 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
ret.append([str(index + 1), item['step_name'], item['actions']])
return Response(ret)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductToOrderSerializer)
@transaction.atomic
def to_order(self, request, pk=None):
"""
指派发货订单
"""
serializer = WproductToOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')])
wp = wps.first()
order = vdata['order']
if wp.material != order.product:
raise exceptions.ValidationError('所选订单与产品不符')
for i in wps:
if i.material != wp.material and i.material.type != Material.MA_TYPE_GOOD and i.act_state \
not in [WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_INM]:
raise exceptions.ValidationError('所选产品错误')
for i in wps:
i.to_order = order
i.update_by = request.user
i.save()
WpmServies.add_wproduct_flow_log(i,change_str='to_order')
return Response()
class WproductTicketViewSet(ListModelMixin, GenericViewSet):
"""
@ -559,6 +583,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
ope = OperationEquip()
ope.operation = op
ope.equip = i
ope.state = i.state
ope.save()
# 查询所需的工具工装
for i in SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL,

View File

@ -47,17 +47,20 @@ class FitJSONRenderer(JSONRenderer):
"""
response_body = BaseResponse()
response = renderer_context.get("response")
response_body.code = response.status_code
if response_body.code >= 400: # 响应异常
status_code = response.status_code # Http状态异常码
if status_code >= 400: # 如果http响应异常
if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码
response_body = data
else:
response_body.data = data # data里是详细异常信息
prefix = ""
if isinstance(data, dict):
prefix = list(data.keys())[0]
data = data[prefix]
if isinstance(data, list):
elif isinstance(data, list):
data = data[0]
response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
else:
response_body.data = data
renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分
renderer_context.get("response").status_code = 200 # 统一成200响应,body里code区分业务异常
return super(FitJSONRenderer, self).render(response_body.dict, accepted_media_type, renderer_context)