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

This commit is contained in:
shilixia 2021-11-10 09:07:43 +08:00
commit 91d82a7882
40 changed files with 1305 additions and 178 deletions

View File

@ -66,7 +66,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-card style="margin-top: 6px"> <el-card style="margin-top: 2px">
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span>日志列表</span> <span>日志列表</span>
</div> </div>

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.6 on 2021-11-08 01:51
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
('qm', '0003_testrecord'),
('inm', '0010_auto_20211102_1631'),
]
operations = [
migrations.RenameModel(
old_name='FIFODetail',
new_name='FIFOItem',
),
]

View File

@ -53,11 +53,15 @@ class FIFO(CommonAModel):
""" """
出入库记录 出入库记录
""" """
FIFO_TYPE_DO_OUT = 1 # 生产领料
FIFO_TYPE_SALE_OUT = 2
FIFO_TYPE_PUR_IN = 3
FIFO_TYPE_DO_IN = 4
type_choices = ( type_choices = (
(1, '生产领料'), (FIFO_TYPE_DO_OUT, '生产领料'),
(2, '销售提货'), (FIFO_TYPE_SALE_OUT, '销售提货'),
(3, '采购入库'), (FIFO_TYPE_PUR_IN, '采购入库'),
(4, '生产入库') (FIFO_TYPE_DO_IN, '生产入库')
) )
type = models.IntegerField('出入库类型', default=1) type = models.IntegerField('出入库类型', default=1)
is_audited = models.BooleanField('是否审核', default=False) is_audited = models.BooleanField('是否审核', default=False)
@ -68,9 +72,9 @@ class FIFO(CommonAModel):
remark = models.CharField('备注', max_length=1000, default='') remark = models.CharField('备注', max_length=1000, default='')
class FIFODetail(BaseModel): class FIFOItem(BaseModel):
""" """
出入库详细记录 出入库详细条目
""" """
is_tested = models.BooleanField('是否已检测', default=False) is_tested = models.BooleanField('是否已检测', default=False)
is_testok = models.BooleanField('是否检测合格', default=False) is_testok = models.BooleanField('是否检测合格', default=False)

View File

@ -1,9 +1,10 @@
from rest_framework import serializers from rest_framework import serializers
from apps.inm.models import FIFO, FIFODetail, IProduct, MaterialBatch, WareHouse,Inventory from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction
class WareHouseSerializer(serializers. ModelSerializer): class WareHouseSerializer(serializers. ModelSerializer):
create_by_=UserSimpleSerializer('create_by', read_only=True) create_by_=UserSimpleSerializer('create_by', read_only=True)
@ -42,10 +43,10 @@ class FIFOListSerializer(serializers.ModelSerializer):
model=FIFO model=FIFO
fields = '__all__' fields = '__all__'
class FIFODetailSerializer(serializers.ModelSerializer): class FIFOItemSerializer(serializers.ModelSerializer):
material_= MaterialSimpleSerializer(source='material', read_only=True) material_= MaterialSimpleSerializer(source='material', read_only=True)
class Meta: class Meta:
model=FIFODetail model= FIFOItem
fields = '__all__' fields = '__all__'
class IProductInPurSerializer(serializers.ModelSerializer): class IProductInPurSerializer(serializers.ModelSerializer):
@ -56,7 +57,7 @@ class IProductInPurSerializer(serializers.ModelSerializer):
class FIFODetailInPurSerializer(serializers.ModelSerializer): class FIFODetailInPurSerializer(serializers.ModelSerializer):
details = IProductInPurSerializer(many=True, required=False) details = IProductInPurSerializer(many=True, required=False)
class Meta: class Meta:
model = FIFODetail model = FIFOItem
fields = ['material', 'count', 'batch', 'details'] fields = ['material', 'count', 'batch', 'details']
class MaterialBatchQuerySerializer(serializers.Serializer): class MaterialBatchQuerySerializer(serializers.Serializer):
@ -91,7 +92,8 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
pass pass
# 创建采购入库 # 创建采购入库
validated_data['type'] = 3 with transaction.atomic():
validated_data['type'] = FIFO.FIFO_TYPE_PUR_IN
obj = FIFO(**validated_data) obj = FIFO(**validated_data)
obj.save() obj.save()
for i in details: for i in details:
@ -101,7 +103,7 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
raise serializers.ValidationError('数目对不上') raise serializers.ValidationError('数目对不上')
else: else:
i['fifo'] = obj i['fifo'] = obj
fifod = FIFODetail.objects.create(**i) fifod = FIFOItem.objects.create(**i)
p_list = [] p_list = []
for x in p_details: for x in p_details:
x['state'] = 1 x['state'] = 1
@ -113,6 +115,6 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
IProduct.objects.bulk_create(p_list) IProduct.objects.bulk_create(p_list)
else: else:
i['fifo'] = obj i['fifo'] = obj
FIFODetail.objects.create(**i) FIFOItem.objects.create(**i)
return obj return obj

View File

@ -1,7 +1,7 @@
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from apps.inm.models import Inventory, MaterialBatch, FIFO, FIFODetail from apps.inm.models import Inventory, MaterialBatch, FIFO, FIFOItem
def update_inm(instance:FIFO, type:int=1): def update_inm(instance:FIFO, type:int=1):
@ -9,9 +9,9 @@ def update_inm(instance:FIFO, type:int=1):
更新库存(正反) 更新库存(正反)
""" """
warehouse = instance.warehouse warehouse = instance.warehouse
if instance.type in [3]: # 采购入库 if instance.type in [FIFO.FIFO_TYPE_PUR_IN]: # 采购入库
# 更新相关表 # 更新相关表
for i in FIFODetail.objects.filter(fifo=instance): for i in FIFOItem.objects.filter(fifo=instance):
material = i.material material = i.material
o1, _ = Inventory.objects.get_or_create(material=material, warehouse=warehouse, \ o1, _ = Inventory.objects.get_or_create(material=material, warehouse=warehouse, \
defaults={'material':material, 'warehouse':warehouse, 'count':0}) defaults={'material':material, 'warehouse':warehouse, 'count':0})
@ -23,9 +23,9 @@ def update_inm(instance:FIFO, type:int=1):
o2.save() o2.save()
material.count = material.count + i.count material.count = material.count + i.count
material.save() material.save()
elif instance.type in [1]: # 生产领料 elif instance.type in [FIFO.FIFO_TYPE_DO_OUT]: # 生产领料
# 更新相关表 # 更新相关表
for i in FIFODetail.objects.filter(fifo=instance): for i in FIFOItem.objects.filter(fifo=instance):
material = i.material material = i.material
o1 = Inventory.objects.get(material=material, warehouse=warehouse) o1 = Inventory.objects.get(material=material, warehouse=warehouse)
o1.count = o1.count - i.count o1.count = o1.count - i.count

View File

@ -1,6 +1,6 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.inm.views import FIFODetailViewSet, FIFOViewSet, MaterialBatchViewSet, WarehouseViewSet,InventoryViewSet from apps.inm.views import FIFOItemViewSet, FIFOViewSet, MaterialBatchViewSet, WarehouseViewSet,InventoryViewSet
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
@ -9,7 +9,7 @@ router.register('warehouse', WarehouseViewSet, basename='warehouse')
router.register('inventory', InventoryViewSet, basename='inventory') router.register('inventory', InventoryViewSet, basename='inventory')
router.register('materialbatch', MaterialBatchViewSet, basename='materialbatch') router.register('materialbatch', MaterialBatchViewSet, basename='materialbatch')
router.register('fifo', FIFOViewSet, basename='fifo'), router.register('fifo', FIFOViewSet, basename='fifo'),
router.register('fifodetail', FIFODetailViewSet, basename='fifodetail') router.register('fifodetail', FIFOItemViewSet, basename='fifodetail')
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,16 +1,17 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.inm.filters import MbFilterSet from apps.inm.filters import MbFilterSet
from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse,Inventory from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse,Inventory
from apps.inm.serializers import FIFODetailSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer
from apps.inm.signals import update_inm from apps.inm.signals import update_inm
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from django.db import transaction
# Create your views here. # Create your views here.
class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet): class WarehouseViewSet(CreateUpdateModelAMixin, ModelViewSet):
@ -63,18 +64,23 @@ class MaterialBatchViewSet(ListModelMixin, GenericViewSet):
queryset = self.queryset.filter(warehouse__id=data['warehouse'], material__id__in=data['materials']) queryset = self.queryset.filter(warehouse__id=data['warehouse'], material__id__in=data['materials'])
return Response(MaterialBatchSerializer(instance=queryset, many=True).data) return Response(MaterialBatchSerializer(instance=queryset, many=True).data)
class FIFODetailViewSet(ListModelMixin, GenericViewSet): class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
""" """
出入库记录详情表 出入库记录详情表
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = FIFODetail.objects.select_related('material', 'fifo').all() queryset = FIFOItem.objects.select_related('material', 'fifo').all()
serializer_class = FIFODetailSerializer serializer_class = FIFOItemSerializer
filterset_fields = ['material', 'fifo'] filterset_fields = ['material', 'fifo']
search_fields = [] search_fields = []
ordering_fields = ['create_time'] ordering_fields = ['create_time']
ordering = ['-create_time'] ordering = ['-create_time']
def perform_destroy(self, instance):
if instance.fifo.is_audited:
raise APIException('该出入库记录已通过审核, 无法删除')
return super().perform_destroy(instance)
class FIFOViewSet(ListModelMixin, GenericViewSet): class FIFOViewSet(ListModelMixin, GenericViewSet):
""" """
出入库记录 出入库记录
@ -108,9 +114,12 @@ class FIFOViewSet(ListModelMixin, GenericViewSet):
审核通过 审核通过
""" """
obj = self.get_object() obj = self.get_object()
for i in FIFODetail.objects.filter(fifo=obj): for i in FIFOItem.objects.filter(fifo=obj):
if not i.is_testok: if not i.is_testok:
raise APIException('未检验通过, 不可审核') raise APIException('未检验通过, 不可审核')
if obj.is_audited:
raise APIException('该入库记录已审核通过')
with transaction.atomic():
obj.is_audited = True obj.is_audited = True
obj.save() obj.save()
update_inm(obj) # 更新库存 update_inm(obj) # 更新库存

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
]
operations = [
migrations.AddField(
model_name='step',
name='type',
field=models.IntegerField(choices=[(1, '普通'), (2, '分割'), (3, '结合')], default=1, verbose_name='操作类型'),
),
]

View File

@ -12,13 +12,19 @@ class Material(CommonAModel):
""" """
物料 物料
""" """
MA_TYPE_GOOD = 1
MA_TYPE_HALFGOOD = 2
MA_TYPE_MAINSO = 3
MA_TYPE_HELPSO = 4
MA_TYPE_TOOL = 5
MA_TYPE_HELPTOOL = 6
type_choices=( type_choices=(
(1, '成品'), (MA_TYPE_GOOD, '成品'),
(2, '半成品'), (MA_TYPE_HALFGOOD, '半成品'),
(3, '主要原料'), (MA_TYPE_MAINSO, '主要原料'),
(4, '辅助材料') , (MA_TYPE_HELPSO, '辅助材料') ,
(5, '加工工具'), (MA_TYPE_TOOL, '加工工具'),
(6, '辅助工装') (MA_TYPE_HELPTOOL, '辅助工装')
) )
unit_choices =( unit_choices =(
('', ''), ('', ''),
@ -62,6 +68,15 @@ class Step(CommonAModel):
""" """
工序步骤 工序步骤
""" """
STEP_TYPE_NOM = 1
STEP_TYPE_DIV = 2
STEP_TYPE_COMB = 3
step_type_choices=(
(STEP_TYPE_NOM, '普通'),
(STEP_TYPE_DIV, '分割'),
(STEP_TYPE_COMB, '结合')
)
type = models.IntegerField('操作类型', choices=step_type_choices, default=1)
process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序', related_name='step_process') process = models.ForeignKey(Process, on_delete=models.CASCADE, verbose_name='所属工序', related_name='step_process')
name = models.CharField('工序步骤名称', max_length=100) name = models.CharField('工序步骤名称', max_length=100)
number = models.CharField('步骤编号', max_length=100, null=True, blank=True) number = models.CharField('步骤编号', max_length=100, null=True, blank=True)
@ -80,9 +95,11 @@ class RecordForm(CommonAModel):
""" """
记录表格 记录表格
""" """
RF_TYPE_DO = 1
RF_TYPE_TEST = 2
type_choices=( type_choices=(
(1, '生产记录'), (RF_TYPE_DO, '生产记录'),
(2, '检验记录') (RF_TYPE_TEST, '检验记录')
) )
name = models.CharField('表格名称', max_length=100, unique=True) name = models.CharField('表格名称', max_length=100, unique=True)
type = models.IntegerField('表格类型', choices=type_choices, default=1) type = models.IntegerField('表格类型', choices=type_choices, default=1)
@ -164,10 +181,13 @@ class SubprodctionMaterial(CommonAModel):
""" """
输入/输出物料/工具工装 输入/输出物料/工具工装
""" """
SUB_MA_TYPE_IN = 1
SUB_MA_TYPE_OUT = 2
SUB_MA_TYPE_TOOL = 3
type_choices=( type_choices=(
(1, '输入物料'), (SUB_MA_TYPE_IN, '输入物料'),
(2, '输出物料'), (SUB_MA_TYPE_OUT, '输出物料'),
(3, '工具工装') (SUB_MA_TYPE_TOOL, '工具工装')
) )
material = models.ForeignKey(Material, verbose_name='物料', on_delete=models.CASCADE, related_name='subplan_material') material = models.ForeignKey(Material, verbose_name='物料', on_delete=models.CASCADE, related_name='subplan_material')
is_main = models.BooleanField('是否主产出', default=False) # 以该产品完成度计算进度 is_main = models.BooleanField('是否主产出', default=False) # 以该产品完成度计算进度

View File

@ -61,6 +61,11 @@ class StepDetailSerializer(serializers.ModelSerializer):
queryset = queryset.prefetch_related('equipments') queryset = queryset.prefetch_related('equipments')
return queryset return queryset
class SubProductionSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SubProduction
fields = ['id', 'name']
class SubProductionSerializer(serializers.ModelSerializer): class SubProductionSerializer(serializers.ModelSerializer):
process_ = ProcessSimpleSerializer(source='process', read_only=True) process_ = ProcessSimpleSerializer(source='process', read_only=True)
class Meta: class Meta:
@ -90,9 +95,9 @@ class InputMaterialSerializer(serializers.ModelSerializer):
fields = ['count', 'sort', 'material', 'subproduction'] fields = ['count', 'sort', 'material', 'subproduction']
def create(self, validated_data): def create(self, validated_data):
if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=1).exists(): if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=SubprodctionMaterial.SUB_MA_TYPE_IN).exists():
raise ValidationError('该物料已存在') raise ValidationError('该物料已存在')
validated_data['type']=1 validated_data['type']=SubprodctionMaterial.SUB_MA_TYPE_IN
return super().create(validated_data) return super().create(validated_data)
class InputMaterialUpdateSerializer(serializers.ModelSerializer): class InputMaterialUpdateSerializer(serializers.ModelSerializer):
@ -106,11 +111,11 @@ class OutputMaterialSerializer(serializers.ModelSerializer):
fields = ['count', 'sort', 'material', 'subproduction', 'is_main'] fields = ['count', 'sort', 'material', 'subproduction', 'is_main']
def create(self, validated_data): def create(self, validated_data):
if SubprodctionMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True, type=2).exists(): if SubprodctionMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exists():
raise ValidationError('主产出只能有1个') raise ValidationError('主产出只能有1个')
if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=2).exists(): if SubprodctionMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exists():
raise ValidationError('该物料已存在') raise ValidationError('该物料已存在')
validated_data['type']=2 validated_data['type']=SubprodctionMaterial.SUB_MA_TYPE_OUT
return super().create(validated_data) return super().create(validated_data)
class OutputMaterialUpdateSerializer(serializers.ModelSerializer): class OutputMaterialUpdateSerializer(serializers.ModelSerializer):

View File

@ -85,7 +85,7 @@ class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
输入物料-增删改查 输入物料-增删改查
""" """
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = SubprodctionMaterial.objects.select_related('material').filter(type=1) queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN)
serializer_class = InputMaterialSerializer serializer_class = InputMaterialSerializer
filterset_fields = ['subproduction'] filterset_fields = ['subproduction']
ordering = ['sort', '-create_time'] ordering = ['sort', '-create_time']
@ -102,7 +102,7 @@ class OutputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
输出物料-增删改查 输出物料-增删改查
""" """
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = SubprodctionMaterial.objects.select_related('material').filter(type=2) queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
serializer_class = OutputMaterialSerializer serializer_class = OutputMaterialSerializer
filterset_fields = ['subproduction'] filterset_fields = ['subproduction']
ordering = ['sort', '-create_time'] ordering = ['sort', '-create_time']
@ -119,7 +119,7 @@ class OtherMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, DestroyModel
其他物料-增删改查 其他物料-增删改查
""" """
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = SubprodctionMaterial.objects.select_related('material').filter(type=3) queryset = SubprodctionMaterial.objects.select_related('material').filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL)
serializer_class = OutputMaterialSerializer serializer_class = OutputMaterialSerializer
filterset_fields = ['subproduction'] filterset_fields = ['subproduction']
ordering = ['sort', '-create_time'] ordering = ['sort', '-create_time']

View File

@ -5,3 +5,5 @@ class SamConfig(AppConfig):
verbose_name = '生产计划管理' verbose_name = '生产计划管理'
def ready(self):
import apps.pm.signals

View File

@ -0,0 +1,53 @@
# Generated by Django 3.2.6 on 2021-11-03 09:00
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
('pm', '0009_auto_20211029_1017'),
]
operations = [
migrations.AddField(
model_name='subproductionplan',
name='main_count',
field=models.IntegerField(default=1, verbose_name='应产出数'),
preserve_default=False,
),
migrations.AddField(
model_name='subproductionplan',
name='main_count_real',
field=models.IntegerField(default=0, verbose_name='实际产出数'),
),
migrations.AddField(
model_name='subproductionplan',
name='main_product',
field=models.ForeignKey(default=19, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='主要产品'),
preserve_default=False,
),
migrations.AddField(
model_name='subproductionprogress',
name='count_pick',
field=models.IntegerField(default=0, verbose_name='实际领用数'),
),
migrations.AddField(
model_name='subproductionprogress',
name='is_main',
field=models.BooleanField(default=False, verbose_name='是否主产出'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count',
field=models.IntegerField(verbose_name='应出入数'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_real',
field=models.IntegerField(default=0, verbose_name='实际消耗/产出数'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.6 on 2021-11-04 02:06
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
('pm', '0010_auto_20211103_1700'),
]
operations = [
migrations.AlterField(
model_name='subproductionplan',
name='main_count',
field=models.IntegerField(default=0, verbose_name='应产出数'),
),
migrations.AlterField(
model_name='subproductionplan',
name='main_product',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='主要产品'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0011_auto_20211104_1006'),
]
operations = [
migrations.AlterField(
model_name='subproductionprogress',
name='type',
field=models.IntegerField(default=((1, '输入物料'), (2, '输出物料'), (3, '工具工装')), verbose_name='物料应用类型'),
),
]

View File

@ -7,7 +7,7 @@ import django.utils.timezone as timezone
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from apps.mtm.models import Material, Process, SubProduction from apps.mtm.models import Material, Process, SubProduction, SubprodctionMaterial
from apps.sam.models import Order from apps.sam.models import Order
class ProductionPlan(CommonAModel): class ProductionPlan(CommonAModel):
@ -43,9 +43,16 @@ class SubProductionPlan(CommonAModel):
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
start_date = models.DateField('计划开工日期') start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期') end_date = models.DateField('计划完工日期')
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE) process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
main_product = models.ForeignKey(Material, verbose_name='主要产品', on_delete=models.CASCADE, null=True, blank=True)
main_count = models.IntegerField('应产出数', default=0)
main_count_real = models.IntegerField('实际产出数', default=0)
steps = models.JSONField('工艺步骤', default=list) steps = models.JSONField('工艺步骤', default=list)
state = models.IntegerField('状态', default=0) state = models.IntegerField('状态', default=0)
start_date_real = models.DateField('实际开工日期', null=True, blank=True) start_date_real = models.DateField('实际开工日期', null=True, blank=True)
end_date_real = models.DateField('实际完工日期', null=True, blank=True) end_date_real = models.DateField('实际完工日期', null=True, blank=True)
@ -58,13 +65,10 @@ class SubProductionProgress(BaseModel):
""" """
子计划生产进度统计表/物料消耗 子计划生产进度统计表/物料消耗
""" """
type_choices=(
(1, '输入物料'),
(2, '输出物料'),
(3, '工具工装')
)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan') subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan')
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
type = models.IntegerField('物料应用类型', default=1) is_main = models.BooleanField('是否主产出', default=False)
count = models.FloatField('应出入数') type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices)
count_real = models.IntegerField('实际出入数', default=0) count = models.IntegerField('应出入数')
count_pick = models.IntegerField('实际领用数', default=0)
count_real = models.IntegerField('实际消耗/产出数', default=0)

View File

@ -1,7 +1,7 @@
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
from rest_framework import serializers from rest_framework import serializers
from apps.sam.serializers import OrderSerializer from apps.sam.serializers import OrderSerializer
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, SubProductionSimpleSerializer
from apps.system.serializers import OrganizationSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer
@ -27,10 +27,15 @@ class ResourceCalListSerializer(serializers.ListSerializer):
class SubProductionPlanListSerializer(serializers.ModelSerializer): class SubProductionPlanListSerializer(serializers.ModelSerializer):
workshop_ = OrganizationSimpleSerializer(source='workshop', read_only=True) workshop_ = OrganizationSimpleSerializer(source='workshop', read_only=True)
process_ = ProcessSimpleSerializer(source='process', read_only=True) process_ = ProcessSimpleSerializer(source='process', read_only=True)
subproduction_ = SubProductionSimpleSerializer(source='subproduction', read_only=True)
main_product_ = MaterialSimpleSerializer(source='main_product', read_only=True)
class Meta: class Meta:
model=SubProductionPlan model=SubProductionPlan
fields = '__all__' fields = '__all__'
class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SubProductionPlan model = SubProductionPlan

View File

@ -0,0 +1,18 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.pm.models import SubProductionProgress
@receiver(post_save, sender=SubProductionProgress)
def update_subplan_main(sender, instance, created, **kwargs):
"""
根据生产进度统计表更新子计划进度表相关字段
"""
if instance.is_main:
subplan = instance.subproduction_plan
if created:
subplan.main_product = instance.material
subplan.main_count = instance.count
subplan.main_count_real = instance.count_real
subplan.save()

View File

@ -87,8 +87,11 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
start_date=production_plan.start_date, end_date=production_plan.end_date, start_date=production_plan.start_date, end_date=production_plan.end_date,
workshop=i.process.workshop, process=i.process, create_by=request.user, workshop=i.process.workshop, process=i.process, create_by=request.user,
steps = list(steps)) steps = list(steps))
# 生成子计划物料需求/进度
for m in SubprodctionMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'): for m in SubprodctionMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
SubProductionProgress.objects.create(material=m.material, type=m.type, count=m.count*production_plan.count, subproduction_plan=instance) spro = SubProductionProgress.objects.create(material=m.material, type=m.type,
is_main=m.is_main,
count=m.count*production_plan.count, subproduction_plan=instance)
production_plan.is_planed=True production_plan.is_planed=True
production_plan.save() production_plan.save()
return Response() return Response()
@ -98,10 +101,10 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
子生产计划-列表/修改 子生产计划-列表/修改
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = SubProductionPlan.objects.select_related('process', 'workshop') queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'main_product')
search_fields = [] search_fields = []
serializer_class = SubProductionPlanListSerializer serializer_class = SubProductionPlanListSerializer
filterset_fields = ['production_plan', 'process', 'state'] filterset_fields = ['production_plan', 'process', 'state', 'main_product', 'workshop']
ordering_fields = ['process__number'] ordering_fields = ['process__number']
ordering = ['process__number'] ordering = ['process__number']
@ -152,7 +155,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
领料需求清单 领料需求清单
""" """
obj = self.get_object() obj = self.get_object()
instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1) instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN)
serializer = SubProductionProgressSerializer(instance=instance, many=True) serializer = SubProductionProgressSerializer(instance=instance, many=True)
return Response(serializer.data) return Response(serializer.data)
@ -162,7 +165,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
领料需求清单/库存数 领料需求清单/库存数
""" """
obj = self.get_object() obj = self.get_object()
instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=1) instance = SubProductionProgress.objects.filter(subproduction_plan=obj, type=SubprodctionMaterial.SUB_MA_TYPE_IN)
serializer = SubProductionProgressSerializer(instance=instance, many=True) serializer = SubProductionProgressSerializer(instance=instance, many=True)
need = serializer.data need = serializer.data
materials = [] materials = []

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.6 on 2021-11-08 01:51
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
('qm', '0003_testrecord'),
]
operations = [
migrations.RenameField(
model_name='testrecord',
old_name='fifo_detail',
new_name='fifo_item',
),
migrations.CreateModel(
name='TestRecordItem',
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='删除标记')),
('field_name', models.CharField(max_length=50, verbose_name='字段名')),
('field_key', models.CharField(max_length=50, verbose_name='字段标识')),
('field_type', models.IntegerField(choices=[(1, '生产记录'), (2, '检验记录')], verbose_name='字段类型')),
('field_value', models.JSONField(blank=True, default=dict, verbose_name='录入值')),
('need_judge', models.BooleanField(default=False, verbose_name='是否需要判定')),
('is_testok', models.BooleanField(default=True, verbose_name='是否合格')),
('form_field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联字段')),
('test_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='qm.testrecord', verbose_name='关联的检测记录')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.6 on 2021-11-08 03:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('qm', '0004_auto_20211108_0951'),
]
operations = [
migrations.RemoveField(
model_name='testrecord',
name='record_data',
),
migrations.AlterField(
model_name='testrecorditem',
name='test_record',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_test_record', to='qm.testrecord', verbose_name='关联的检测记录'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 03:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0005_auto_20211108_1102'),
]
operations = [
migrations.AddField(
model_name='testrecorditem',
name='sort',
field=models.IntegerField(default=1, verbose_name='排序号'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 05:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0006_testrecorditem_sort'),
]
operations = [
migrations.AlterField(
model_name='testrecorditem',
name='field_type',
field=models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型'),
),
]

View File

@ -1,5 +1,8 @@
from django.db import models from django.db import models
from django.db.models.enums import Choices
from apps.mtm.models import RecordForm, RecordFormField
from apps.system.models import CommonAModel, File from apps.system.models import CommonAModel, File
from utils.model import BaseModel
# Create your models here. # Create your models here.
class Standard(CommonAModel): class Standard(CommonAModel):
""" """
@ -44,9 +47,21 @@ class TestRecord(CommonAModel):
""" """
检验记录 检验记录
""" """
form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE) form = models.ForeignKey('mtm.recordform', verbose_name='所用表格', on_delete=models.CASCADE)
record_data = models.JSONField('记录数据', default=dict, blank=True)
is_testok = models.BooleanField('是否合格', default=True) is_testok = models.BooleanField('是否合格', default=True)
fifo_detail = models.ForeignKey('inm.fifodetail', 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)
class TestRecordItem(BaseModel):
"""
记录表格字段值
"""
form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
field_name = models.CharField('字段名', max_length=50)
field_key = models.CharField('字段标识', max_length=50)
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
field_value = models.JSONField('录入值', default=dict, blank=True)
need_judge = models.BooleanField('是否需要判定', default=False)
sort = models.IntegerField('排序号', default=1)
is_testok = models.BooleanField('是否合格', default=True)
test_record = models.ForeignKey(TestRecord, verbose_name='关联的检测记录', on_delete=models.CASCADE, related_name='item_test_record')

View File

@ -2,7 +2,7 @@ from rest_framework import serializers
from apps.mtm.models import RecordForm, RecordFormField from apps.mtm.models import RecordForm, RecordFormField
from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer
from apps.system.serializers import FileSimpleSerializer from apps.system.serializers import FileSimpleSerializer
from .models import Standard, TestItem, TestRecord from .models import Standard, TestItem, TestRecord, TestRecordItem
class StandardCreateUpdateSerializer(serializers.ModelSerializer): class StandardCreateUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -35,15 +35,21 @@ class AnalysisItemSerializer(serializers.ModelSerializer):
pass pass
class TestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value']
class TestRecordItemSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = '__all__'
class TestRecordCreateSerializer(serializers.ModelSerializer): class TestRecordCreateSerializer(serializers.ModelSerializer):
record_data = TestRecordItemCreateSerializer(many=True)
class Meta: class Meta:
model = TestRecord model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'fifo_detail'] fields = ['form', 'record_data', 'is_testok', 'fifo_item']
def create(self, validated_data):
if 'is_testok' not in validated_data:
raise serializers.ValidationError('未填写检测结论')
return super().create(validated_data)
class TestRecordListSerializer(serializers.ModelSerializer): class TestRecordListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -53,21 +59,21 @@ class TestRecordListSerializer(serializers.ModelSerializer):
class TestRecordDetailSerializer(serializers.ModelSerializer): class TestRecordDetailSerializer(serializers.ModelSerializer):
form_ = RecordFormSimpleSerializer(source='form', read_only=True) form_ = RecordFormSimpleSerializer(source='form', read_only=True)
record_data_ = serializers.SerializerMethodField() record_data_ = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True)
class Meta: class Meta:
model = TestRecord model = TestRecord
fields = '__all__' fields = '__all__'
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):
queryset = queryset.select_related('form','fifo_detail') queryset = queryset.select_related('form','fifo_item')
return queryset return queryset
def get_record_data_(self, obj): # def get_record_data_(self, obj):
record_data = obj.record_data # record_data = obj.record_data
all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort') # all_fields = RecordFormField.objects.filter(form=obj.form, is_deletd=False).order_by('sort')
all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data # all_fields_l = RecordFormFieldSerializer(instance=all_fields, many=True).data
for i in all_fields_l: # for i in all_fields_l:
key = i['field_key'] # key = i['field_key']
i['field_value'] = record_data.get(key, None) # i['field_value'] = record_data.get(key, None)
return all_fields_l # return all_fields_l

View File

@ -1,8 +1,12 @@
from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer
from apps.qm.models import Standard, TestItem, TestRecord from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem
from django.shortcuts import render from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework import status
from django.db import transaction
# Create your views here. # Create your views here.
class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet): class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet):
""" """
@ -43,7 +47,7 @@ class TestRecordViewSet(ModelViewSet):
检测记录 检测记录
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = TestRecord.objects.select_related('fifo_detail', 'form').all() queryset = TestRecord.objects.select_related('fifo_item', 'form').all()
serializer_class = TestRecordListSerializer serializer_class = TestRecordListSerializer
ordering = ['-id'] ordering = ['-id']
@ -56,10 +60,33 @@ class TestRecordViewSet(ModelViewSet):
return TestRecordDetailSerializer return TestRecordDetailSerializer
return super().get_serializer_class() return super().get_serializer_class()
def perform_create(self, serializer): def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
record_data = vdata.pop('record_data')
if 'is_testok' not in vdata:
raise APIException('未填写检测结论')
with transaction.atomic():
obj = serializer.save(create_by = self.request.user) obj = serializer.save(create_by = self.request.user)
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 True
m['test_record'] = obj
tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris)
# 如果检测合格 # 如果检测合格
if obj.fifo_detail: if obj.fifo_item:
obj.fifo_detail.is_testok = True if obj.is_testok else False obj.fifo_item.is_testok = True if obj.is_testok else False
obj.fifo_detail.is_tested = True obj.fifo_item.is_tested = True
obj.fifo_detail.save() obj.fifo_item.save()
return Response(status=status.HTTP_201_CREATED)

View File

@ -30,7 +30,6 @@ class Migration(migrations.Migration):
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('m_state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')), ('m_state', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')),
('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤')), ('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤')),
('parent', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='上一级')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')), ('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子生产计划')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproduct_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
], ],

View File

@ -0,0 +1,61 @@
# Generated by Django 3.2.6 on 2021-11-04 01:14
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 = [
('pm', '0010_auto_20211103_1700'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0028_auto_20211102_1707'),
('wpm', '0003_auto_20211102_0935'),
]
operations = [
migrations.CreateModel(
name='WProductRecord',
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='删除标记')),
('data', models.JSONField(blank=True, default=dict, verbose_name='记录的数据')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('record_form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用表格')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.RemoveField(
model_name='wmaterial',
name='process',
),
migrations.RemoveField(
model_name='wmaterial',
name='workshop',
),
migrations.RemoveField(
model_name='wproductflow',
name='wproduct',
),
migrations.AddField(
model_name='wmaterial',
name='subproduction_plan',
field=models.ForeignKey(default=68, on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='关联子计划'),
preserve_default=False,
),
migrations.AddField(
model_name='wproductflow',
name='wproducts',
field=models.JSONField(default=list, verbose_name='关联产品'),
),
migrations.DeleteModel(
name='WProductForm',
),
]

View File

@ -0,0 +1,109 @@
# Generated by Django 3.2.6 on 2021-11-08 01:01
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),
('mtm', '0028_auto_20211102_1707'),
('wpm', '0004_auto_20211104_0914'),
]
operations = [
migrations.CreateModel(
name='WProductAction',
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='删除标记')),
('wproducts', models.JSONField(blank=True, default=list, verbose_name='关联产品ID列表')),
('remark', 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='wproductaction_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('m_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='操作时的物料状态')),
('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='操作步骤')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductaction_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='WProductMaterial',
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='删除标记')),
('type', models.IntegerField(choices=[(1, '消耗'), (2, '产出')], default=0, verbose_name='类型')),
('count', models.IntegerField(verbose_name='消耗或产出数量')),
('material', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='可能产出的副产品')),
('wmaterial', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wmaterial', verbose_name='关联的车间物料')),
('wproduct_action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductaction', verbose_name='关联的生产操作')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='WProductRecordDetail',
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='删除标记')),
('field_name', models.CharField(max_length=50, verbose_name='字段名')),
('field_key', models.CharField(max_length=50, verbose_name='字段标识')),
('field_type', models.IntegerField(choices=[(1, '生产记录'), (2, '检验记录')], verbose_name='字段类型')),
('field_value', models.JSONField(blank=True, default=dict, verbose_name='录入值')),
('sort', models.IntegerField(default=1, verbose_name='排序号')),
('form_field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联字段')),
],
options={
'abstract': False,
},
),
migrations.DeleteModel(
name='WProductFlow',
),
migrations.RemoveField(
model_name='wproductrecord',
name='data',
),
migrations.RemoveField(
model_name='wproductrecord',
name='record_form',
),
migrations.AddField(
model_name='wproduct',
name='is_executed',
field=models.BooleanField(default=False, verbose_name='子工序是否已执行'),
),
migrations.AddField(
model_name='wproductrecord',
name='form',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用的生产记录表格'),
preserve_default=False,
),
migrations.AlterField(
model_name='wproduct',
name='act_state',
field=models.IntegerField(choices=[(1, '生产中'), (2, '待检测'), (3, '已合格')], default=0, verbose_name='进行状态'),
),
migrations.AddField(
model_name='wproductrecorddetail',
name='wproduct_record',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductrecord', verbose_name='关联的生产记录'),
),
migrations.AddField(
model_name='wproductrecord',
name='wproduct_action',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductaction', verbose_name='关联的生产操作'),
preserve_default=False,
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 01:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('mtm', '0028_auto_20211102_1707'),
('wpm', '0005_auto_20211108_0901'),
]
operations = [
migrations.RenameModel(
old_name='WProductRecordDetail',
new_name='WProductRecordItem',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 05:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0006_rename_wproductrecorddetail_wproductrecorditem'),
]
operations = [
migrations.AlterField(
model_name='wproductrecorditem',
name='field_type',
field=models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0007_alter_wproductrecorditem_field_type'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='is_hidden',
field=models.BooleanField(default=False, verbose_name='是否隐藏'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-11-08 15:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0008_wproduct_is_hidden'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='parent',
field=models.JSONField(blank=True, default=list, verbose_name=''),
),
]

View File

@ -0,0 +1,136 @@
# Generated by Django 3.2.6 on 2021-11-08 15:35
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 = [
('mtm', '0029_step_type'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('wpm', '0009_wproduct_parent'),
]
operations = [
migrations.CreateModel(
name='Operation',
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='删除标记')),
('wproducts', models.JSONField(blank=True, default=list, verbose_name='关联产品ID列表')),
('remark', 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='operation_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('m_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='操作时的物料状态')),
('p_state', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='操作步骤')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operation_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='OperationMaterial',
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='删除标记')),
('type', models.IntegerField(choices=[(1, '消耗'), (2, '产出')], default=0, verbose_name='类型')),
('count', models.IntegerField(verbose_name='消耗或产出数量')),
('material', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='可能产出的副产品')),
('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产操作')),
('wmaterial', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wmaterial', verbose_name='关联的车间物料')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='OperationRecord',
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='删除标记')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operationrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.recordform', verbose_name='所用的生产记录表格')),
('operation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产操作')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operationrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='OperationRecordItem',
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='删除标记')),
('field_name', models.CharField(max_length=50, verbose_name='字段名')),
('field_key', models.CharField(max_length=50, verbose_name='字段标识')),
('field_type', models.CharField(choices=[(1, '生产记录'), (2, '检验记录')], max_length=50, verbose_name='字段类型')),
('field_value', models.JSONField(blank=True, default=dict, verbose_name='录入值')),
('sort', models.IntegerField(default=1, verbose_name='排序号')),
('form_field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='mtm.recordformfield', verbose_name='关联字段')),
('operation_record', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operation', verbose_name='关联的生产记录')),
],
options={
'abstract': False,
},
),
migrations.RemoveField(
model_name='wproductmaterial',
name='material',
),
migrations.RemoveField(
model_name='wproductmaterial',
name='wmaterial',
),
migrations.RemoveField(
model_name='wproductmaterial',
name='wproduct_action',
),
migrations.RemoveField(
model_name='wproductrecord',
name='create_by',
),
migrations.RemoveField(
model_name='wproductrecord',
name='form',
),
migrations.RemoveField(
model_name='wproductrecord',
name='update_by',
),
migrations.RemoveField(
model_name='wproductrecord',
name='wproduct_action',
),
migrations.RemoveField(
model_name='wproductrecorditem',
name='form_field',
),
migrations.RemoveField(
model_name='wproductrecorditem',
name='wproduct_record',
),
migrations.DeleteModel(
name='WProductAction',
),
migrations.DeleteModel(
name='WProductMaterial',
),
migrations.DeleteModel(
name='WProductRecord',
),
migrations.DeleteModel(
name='WProductRecordItem',
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.6 on 2021-11-09 05:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0010_auto_20211108_2335'),
]
operations = [
migrations.AlterField(
model_name='operationrecorditem',
name='operation_record',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.operationrecord', verbose_name='关联的生产记录'),
),
]

View File

@ -6,14 +6,13 @@ from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from apps.mtm.models import Material, Process, Step, RecordForm from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm
class WMaterial(BaseModel): class WMaterial(BaseModel):
""" """
车间生产物料 车间生产物料
""" """
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子计划', on_delete=models.CASCADE)
process = models.ForeignKey(Process, 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)
batch = models.CharField('批次号', max_length=100, null=True, blank=True) batch = models.CharField('批次号', max_length=100, null=True, blank=True)
count = models.IntegerField('当前数量', default=0) count = models.IntegerField('当前数量', default=0)
@ -22,30 +21,66 @@ class WProduct(CommonAModel):
""" """
半成品/成品 半成品/成品
""" """
WPR_ACT_STATE_DOING = 1
WPR_ACT_STATE_TOTEST = 2
WPR_ACT_STATE_OK = 3
act_state_choices=( act_state_choices=(
(0, '待执行'), (WPR_ACT_STATE_DOING, '生产中'),
(1, '进行中'), (WPR_ACT_STATE_TOTEST, '待检测'),
(2, '已完成') (WPR_ACT_STATE_OK, '已合格')
) )
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True) p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True)
act_state = models.IntegerField('进行状态', default=0) act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices)
parent = models.ForeignKey('self', verbose_name='上一级', on_delete=models.CASCADE, db_constraint=False) is_executed = models.BooleanField('子工序是否已执行', default=False)
is_hidden = models.BooleanField('是否隐藏', default=False)
parent = models.JSONField('', default=list, blank=True)
remark = models.CharField('备注', max_length=200, null=True, blank=True) remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE)
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE) production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
class WProductForm(CommonAModel):
class Operation(CommonAModel):
"""
生产操作
"""
wproducts = models.JSONField('关联产品ID列表', default=list, blank=True)
m_state = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE, null=True, blank=True)
p_state = models.ForeignKey(Step, verbose_name='操作步骤', on_delete=models.CASCADE, null=True, blank=True)
remark = models.CharField('操作备注', max_length=200, null=True, blank=True)
class OperationMaterial(BaseModel):
"""
车间生产物料消耗产出表
"""
type_choices=(
(1, '消耗'),
(2, '产出')
)
type = models.IntegerField('类型', default=0, choices=type_choices)
operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE)
wmaterial = models.ForeignKey(WMaterial, verbose_name='关联的车间物料', on_delete=models.CASCADE, null=True, blank=True)
material = models.ForeignKey(Material, verbose_name='可能产出的副产品', on_delete=models.CASCADE, null=True, blank=True)
count = models.IntegerField('消耗或产出数量')
class OperationRecord(CommonAModel):
""" """
记录表格 记录表格
""" """
record_form = models.ForeignKey(RecordForm, verbose_name='所用表格', on_delete=models.CASCADE) form = models.ForeignKey(RecordForm, verbose_name='所用的生产记录表格', on_delete=models.CASCADE)
data = models.JSONField('记录的数据', default=dict, blank=True) operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE)
class WProductFlow(BaseModel): class OperationRecordItem(BaseModel):
""" """
产品流转日志 记录表格字段值
""" """
wproduct = models.ForeignKey(WProduct, verbose_name='产品', on_delete=models.CASCADE) form_field = models.ForeignKey(RecordFormField, verbose_name='关联字段', on_delete=models.CASCADE, db_constraint=False)
field_name = models.CharField('字段名', max_length=50)
field_key = models.CharField('字段标识', max_length=50)
field_type = models.CharField('字段类型', choices=RecordForm.type_choices, max_length=50)
field_value = models.JSONField('录入值', default=dict, blank=True)
sort = models.IntegerField('排序号', default=1)
operation_record = models.ForeignKey(OperationRecord, verbose_name='关联的生产记录', on_delete=models.CASCADE)

View File

@ -1,14 +1,16 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from apps.inm.models import FIFO, FIFODetail, MaterialBatch, WareHouse from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse
from apps.inm.signals import update_inm from apps.inm.signals import update_inm
from apps.mtm.models import Material from apps.mtm.models import Material, RecordForm, Step
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer, StepSimpleSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.models import SubProductionPlan, SubProductionProgress
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from apps.wpm.models import WMaterial from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem
from django.db import transaction
class PickDetailSerializer(serializers.Serializer): class PickDetailSerializer(serializers.Serializer):
material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID") material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label="物料ID")
@ -23,6 +25,8 @@ class PickSerializer(serializers.Serializer):
def create(self, validated_data): def create(self, validated_data):
picks = validated_data.pop('picks') picks = validated_data.pop('picks')
sp = validated_data['subproduction_plan'] sp = validated_data['subproduction_plan']
if sp.state not in [1,2]:
raise serializers.ValidationError('该子计划状态错误')
if sp.is_picked: if sp.is_picked:
raise serializers.ValidationError('该子计划已领料') raise serializers.ValidationError('该子计划已领料')
for i in picks: for i in picks:
@ -33,33 +37,36 @@ class PickSerializer(serializers.Serializer):
except: except:
raise serializers.ValidationError('物料不存在') raise serializers.ValidationError('物料不存在')
# 创建出库记录 # 创建出库记录
with transaction.atomic():
operator = self.context['request'].user operator = self.context['request'].user
validated_data['create_by'] = operator validated_data['create_by'] = operator
validated_data['operator'] = operator validated_data['operator'] = operator
validated_data['type'] = 1 validated_data['type'] = FIFO.FIFO_TYPE_DO_OUT
validated_data['inout_date'] = timezone.now() validated_data['inout_date'] = timezone.now()
fifo = FIFO.objects.create(**validated_data) fifo = FIFO.objects.create(**validated_data)
for i in picks: for i in picks:
# 更新出库详情 # 更新出库详情
i['fifo'] = fifo i['fifo'] = fifo
i['count'] = i.pop('pick_count') i['count'] = i.pop('pick_count')
FIFODetail.objects.create(**i) i['is_testok'] = True # 默认检测合格
FIFOItem.objects.create(**i)
# 更新车间物料 # 更新车间物料
wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \ wm, _ = WMaterial.objects.get_or_create(material=i['material'], batch=i['batch'], \
process=sp.process,defaults={ subproduction_plan=sp,defaults={
'material':i['material'], 'material':i['material'],
'batch':i['batch'], 'batch':i['batch'],
'process':sp.process, 'subproduction_plan':sp,
'workshop':sp.workshop,
'count':0 'count':0
}) })
wm.count = wm.count + i['count'] wm.count = wm.count + i['count']
wm.save() wm.save()
# 更新子计划物料情况 # 更新子计划物料情况
spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1) spp = SubProductionProgress.objects.get(material=i['material'], subproduction_plan=sp, type=1)
spp.count_real = spp.count_real + i['count'] spp.count_pick = spp.count_pick + i['count']
spp.save() spp.save()
sp.is_picked=True sp.is_picked=True
sp.state = 3 #生产中
sp.state_date_real = timezone.now() #实际开工日期
sp.save() sp.save()
# 更新库存 # 更新库存
fifo.is_audited = True fifo.is_audited = True
@ -75,3 +82,98 @@ class WMaterialListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = WMaterial model = WMaterial
fields = '__all__' fields = '__all__'
class WProductListSerializer(serializers.ModelSerializer):
"""
半成品列表
"""
m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True)
p_state_ = StepSimpleSerializer(source='p_state', read_only=True)
class Meta:
model = WProduct
fields = '__all__'
class OperationDetailSerializer(serializers.ModelSerializer):
wproducts_ = serializers.SerializerMethodField()
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True)
p_state_ = StepSimpleSerializer(source='p_state', read_only=True)
class Meta:
model = Operation
fields = '__all__'
def get_wproducts_(self, obj):
return list(WProduct.objects.filter(id__in=obj.wproducts).values('id', 'number'))
class OperationListSerializer(serializers.ModelSerializer):
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
m_state_ = MaterialSimpleSerializer(source='m_state', read_only=True)
p_state_ = StepSimpleSerializer(source='p_state', read_only=True)
class Meta:
model = Operation
fields = '__all__'
class OperationInitSerializer(serializers.Serializer):
step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID")
subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID")
wproducts = serializers.ListField(child=
serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False)
def validate(self, data):
subproduction_plan = data['subproduction_plan']
step = data['step']
stepIds=[i['id'] for i in subproduction_plan.steps]
if step.id not in stepIds:
raise serializers.ValidationError('请选择正确的子工序操作')
if 'wproducts' in data and data['wproducts']:
if step.type == Step.STEP_TYPE_DIV:
raise serializers.ValidationError(_('不可进行此操作'))
for i in data['wproducts']:
if i.is_executed:
raise serializers.ValidationError('不可进行操作')
if i.subproduction_plan != subproduction_plan:
raise serializers.ValidationError('半成品所属子计划不一致')
if i.p_state != step:
raise serializers.ValidationError('半成品所属子工序不一致')
else:
if step.type != Step.STEP_TYPE_DIV:
raise serializers.ValidationError(_('请选择半成品进行操作'))
return data
class DoInputSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=WMaterial.objects.all(), label='车间物料ID')
count_input = serializers.IntegerField(min_value=0, label='消耗数量')
class DoOutputSerializer(serializers.Serializer):
material = serializers.PrimaryKeyRelatedField(queryset=Material.objects.all(), label='物料ID')
count_output = serializers.IntegerField(min_value=0, label='产出数量')
class OperationRecordItemSerializer(serializers.ModelSerializer):
class Meta:
model = OperationRecordItem
fields = ['form_field', 'field_value']
class OperationRecordSerializer(serializers.ModelSerializer):
record_data = OperationRecordItemSerializer(many=True)
class Meta:
model = OperationRecord
fields = ['form', 'record_data']
class OperationSubmitSerializer(serializers.Serializer):
step = serializers.PrimaryKeyRelatedField(queryset=Step.objects.all(), label="子工序ID")
subproduction_plan = serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID")
wproducts = serializers.ListField(child=
serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all()), label="半成品ID列表", required=False)
input = DoInputSerializer(many=True, required=False)
output = DoOutputSerializer(many=True, required=False)
forms = OperationRecordSerializer(many=True, required=False)
remark = serializers.CharField(required=False, label='操作备注')

View File

@ -3,11 +3,16 @@ from rest_framework import urlpatterns
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.wpm.views import WMaterialViewSet from apps.wpm.views import DoFormInit, DoFormSubmit, OperationViewSet, WMaterialViewSet, WPlanViewSet, WProductViewSet
router = DefaultRouter() router = DefaultRouter()
router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
router.register('wproduct', WProductViewSet, basename='wproduct')
router.register('operation', OperationViewSet, basename='operation')
router.register('subplan', WPlanViewSet, basename='wplan')
urlpatterns = [ urlpatterns = [
path('do/init/', DoFormInit.as_view()),
path('do/submit/', DoFormSubmit.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,17 +1,35 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.mixins import ListModelMixin from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.utils import serializer_helpers
from rest_framework.utils.field_mapping import get_relation_kwargs from rest_framework.utils.field_mapping import get_relation_kwargs
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from apps.pm.serializers import SubProductionPlanUpdateSerializer from apps.mtm.models import Material, RecordForm, Step, SubprodctionMaterial
from apps.mtm.serializers import RecordFormDetailSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action from rest_framework.decorators import action
from apps.wpm.models import WMaterial from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
from apps.wpm.serializers import PickSerializer, WMaterialListSerializer from apps.wpm.serializers import OperationDetailSerializer, OperationListSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer
from rest_framework.response import Response from rest_framework.response import Response
from django.db import transaction
# Create your views here. # Create your views here.
class WPlanViewSet(ListModelMixin, GenericViewSet):
"""
车间生产计划
"""
perms_map = {'*': '*'}
queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'main_product').exclude(state=0)
search_fields = []
serializer_class = SubProductionPlanListSerializer
filterset_fields = ['production_plan', 'process', 'state', 'main_product', 'workshop']
ordering_fields = ['process__number']
ordering = ['process__number']
class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet): class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
""" """
车间物料表 车间物料表
@ -19,7 +37,7 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
perms_map={'*':'*'} perms_map={'*':'*'}
queryset = WMaterial.objects.select_related('material').all() queryset = WMaterial.objects.select_related('material').all()
serializer_class = WMaterialListSerializer serializer_class = WMaterialListSerializer
filterset_fields = ['material', 'process', 'workshop'] filterset_fields = ['material', 'subproduction_plan', 'subproduction_plan__process', 'subproduction_plan__workshop']
ordering_fields = ['material__number'] ordering_fields = ['material__number']
ordering = ['material__number'] ordering = ['material__number']
@ -33,8 +51,209 @@ class WMaterialViewSet(CreateUpdateModelAMixin, ListModelMixin, GenericViewSet):
serializer.save() serializer.save()
return Response() return Response()
class DoFormInit(CreateAPIView): class WProductViewSet(ListModelMixin, GenericViewSet):
""" """
生产操作表单创建 半成品
""" """
perms_map={'*':'*'} perms_map={'*':'*'}
queryset = WProduct.objects.select_related('p_state', 'm_state').filter(is_hidden=False)
serializer_class = WProductListSerializer
filterset_fields = ['p_state', 'subproduction_plan', 'm_state', 'production_plan', 'p_state__process']
search_fields = ['number']
ordering_fields = ['id']
ordering = ['id']
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PickSerializer)
def test():
pass
class OperationViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
生产操作记录
"""
perms_map={'*':'*'}
queryset = Operation.objects.select_related('p_state', 'm_state').all()
serializer_class = OperationListSerializer
filterset_fields = ['p_state', 'm_state']
ordering_fields = ['id']
ordering = ['-id']
def get_serializer_class(self):
if self.action == 'retrieve':
return OperationDetailSerializer
return super().get_serializer_class()
class DoFormInit(CreateAPIView, GenericAPIView):
perms_map={'*':'*'}
serializer_class=OperationInitSerializer
def post(self, request, format=None):
"""
调用操作表单
"""
data = request.data
serializer = OperationInitSerializer(data=data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
ret = {}
ret_0 = {}
ret_0['step'] = data['step']
ret_0['subproduction_plan'] = data['subproduction_plan']
if 'wproducts' in data and data['wproducts']:
ret_0['wproducts'] = data['wproducts']
else:
ret_0['wproducts'] = []
# 调出该子计划现有物料
ret_0['input'] = list(WMaterial.objects.filter(subproduction_plan=vdata['subproduction_plan'])\
.values('id', 'material', 'material__name', 'count', 'material__number', 'batch'))
for i in ret_0['input']:
i['count_input'] = 0
# 需要输出的物料
if ret_0['wproducts']:
# 排除主要产物, 因为已经放到半成品里了, 由半成品进行处理, 夹层可能需要特殊处理
o_objs = SubProductionProgress.objects.filter(
subproduction_plan=vdata['subproduction_plan'], type=SubprodctionMaterial.SUB_MA_TYPE_OUT).exclude(is_main=True)
else:
o_objs = SubProductionProgress.objects.filter(
subproduction_plan=vdata['subproduction_plan'], type=SubprodctionMaterial.SUB_MA_TYPE_OUT)
ret_0['output'] = list(o_objs.values('material', 'material__name', 'material__number'))
for i in ret_0['output']:
i['count_output']=0
ret['forms'] = []
ret_0['id'] = 0
ret_0['name'] = '基本信息'
ret['forms'].append(ret_0)
forms = RecordForm.objects.filter(step=vdata['step'], type=RecordForm.RF_TYPE_DO)
if forms.exists():
ret['forms'].extend(RecordFormDetailSerializer(instance=forms, many=True).data)
return Response(ret)
class DoFormSubmit(CreateAPIView, GenericAPIView):
perms_map={'*':'*'}
serializer_class = OperationSubmitSerializer
@transaction.atomic
def post(self, request, format=None):
"""
提交操作表单
"""
data = request.data
serializer = OperationSubmitSerializer(data=data, context={'request':self.request})
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data #校验之后的数据
# 创建一个生产操作记录
action_obj = Operation()
action_obj.p_state = vdata['step']
if 'wproducts' in data and data['wproducts']:
action_obj.wproducts = data['wproducts']
action_obj.m_state = vdata['wproducts'][0].m_state
action_obj.remark = vdata.get('remark', '') # 操作备注
action_obj.create_by = request.user
action_obj.save()
# 保存物料消耗
for i in vdata['input']:
if i['count_input']: #如果有消耗
i_wmat = i['id']
OperationMaterial.objects.create(type=1, operation=action_obj,
wmaterial= i_wmat, count=i['count_input'])
# 更新车间物料
i_wmat.count = i_wmat.count- i['count_input']
i_wmat.save()
# 更新子计划物料消耗情况
sp = SubProductionProgress.objects.get(subproduction_plan=i_wmat.subproduction_plan,
material=i_wmat.material)
sp.count_real = sp.count_real + i['count_input']
sp.save()
# 物料产出
if 'output' in data and data['output']:
for i in vdata['output']: # 已经序列化好的数据
ma = i['material']
if vdata['subproduction_plan'].main_product == ma: # 如果是该计划主产物
# 如果是切割
# 获取下一步子工序
if vdata['step'].type == Step.STEP_TYPE_DIV:
stepIds = [i['id'] for i in vdata['subproduction_plan'].steps]
pindex = stepIds.index(vdata['step'].id)
wpr = dict(m_state=ma, p_state=Step.objects.get(pk=stepIds[pindex+1]),
act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='',
subproduction_plan=vdata['subproduction_plan'],
production_plan=vdata['subproduction_plan'].production_plan)
for x in range(i['count_output']):
WProduct.objects.create(**wpr)
else:
# 更新操作产出物料表
OperationMaterial.objects.create(type=2, operation=action_obj,
material= ma, count=i['count_output'])
# 更新车间物料表
ins, _ = WMaterial.objects.get_or_create(subproduction_plan=vdata['subproduction_plan'],
material=ma)
ins.count = ins.count + i['count_output']
ins.save()
# 更新子计划进度表
sp = SubProductionProgress.objects.get(subproduction_plan=vdata['subproduction_plan'],
material=ma)
sp.count_real = sp.count_real + i['count_input']
sp.save()
# 更新动态产品表
if 'wproducts' in data and data['wproducts']:
wproducts = WProduct.objects.filter(pk__in=data['wproducts'])
# 获取下一步子工序
stepIds = [i['id'] for i in vdata['subproduction_plan'].steps]
pindex = stepIds.index(vdata['step'].id)
if pindex + 1 < len(stepIds): # 如果不是最后一步
newstep = Step.objects.get(pk=stepIds[pindex+1])
wproducts.update(p_state=newstep, is_executed=False)
# 特殊情况如果是夹层结合
if vdata['step'].type == Step.STEP_TYPE_COMB:
wproducts.update(is_hidden=True) # 隐藏
WProduct.objects.create(
m_state=vdata['subproduction_plan'].main_product, p_state = newstep,
act_state=WProduct.WPR_ACT_STATE_DOING, is_executed=False, remark='',
subproduction_plan=vdata['subproduction_plan'],
production_plan=vdata['subproduction_plan'].production_plan,
parent = data['wproducts']
)
else: # 如果是最后一步, 此时需要转序并更新状态为待检测
newstep = vdata['step']
wproducts.update(p_state=newstep, is_executed=True, act_state=WProduct.WPR_ACT_STATE_TOTEST)
# 特殊情况如果是夹层结合
if vdata['step'].type == Step.STEP_TYPE_COMB:
wproducts.update(is_hidden=True) # 隐藏
WProduct.objects.create(
m_state=vdata['subproduction_plan'].main_product, p_state = newstep,
act_state=WProduct.WPR_ACT_STATE_TOTEST, is_executed=True, remark='',
subproduction_plan=vdata['subproduction_plan'],
production_plan=vdata['subproduction_plan'].production_plan
)
# 保存自定义表单结果
for i in vdata['forms']:
wr = OperationRecord()
wr.form = i['form']
wr.create_by = request.user
wr.operation = action_obj
wr.save()
wrds = []
for m in i['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['operation_record'] = wr
wrds.append(OperationRecordItem(**m))
OperationRecordItem.objects.bulk_create(wrds)
return Response()

View File

@ -2,9 +2,8 @@ from django.db import models
import django.utils.timezone as timezone import django.utils.timezone as timezone
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
# 自定义软删除查询基类 # 自定义软删除查询基类
class SoftDeletableQuerySetMixin(object): class SoftDeletableQuerySetMixin(object):
''' '''
QuerySet for SoftDeletableModel. Instead of removing instance sets QuerySet for SoftDeletableModel. Instead of removing instance sets
@ -63,6 +62,24 @@ class BaseModel(models.Model):
class Meta: class Meta:
abstract = True abstract = True
def save(self, *args, **kwargs):
if self.pk:
# If self.pk is not None then it's an update.
cls = self.__class__
old = cls.objects.get(pk=self.pk)
# This will get the current model state since super().save() isn't called yet.
new = self # This gets the newly instantiated Mode object with the new values.
changed_fields = []
for field in cls._meta.get_fields():
field_name = field.name
try:
if getattr(old, field_name) != getattr(new, field_name):
changed_fields.append(field_name)
except Exception as ex: # Catch field does not exist exception
pass
kwargs['update_fields'] = changed_fields
super().save(*args, **kwargs)
class SoftModel(BaseModel): class SoftModel(BaseModel):
""" """
软删除基本表 软删除基本表