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

This commit is contained in:
shilixia 2021-11-12 08:36:47 +08:00
commit f63bb65a9e
16 changed files with 332 additions and 91 deletions

View File

@ -122,4 +122,4 @@ body .el-table th.gutter{
} }
.el-dialog__footer{ .el-dialog__footer{
padding: 6px 6px 6px; padding: 6px 6px 6px;
} }

View File

@ -0,0 +1,40 @@
# Generated by Django 3.2.6 on 2021-11-11 01:18
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inm', '0013_alter_materialbatch_batch'),
]
operations = [
migrations.RemoveField(
model_name='fifo',
name='warehouse',
),
migrations.AddField(
model_name='fifoitem',
name='warehouse',
field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.CASCADE, to='inm.warehouse', verbose_name='仓库'),
preserve_default=False,
),
migrations.AlterField(
model_name='fifoitem',
name='count',
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='数量'),
),
migrations.AlterField(
model_name='inventory',
name='count',
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='仓库物料存量'),
),
migrations.AlterField(
model_name='materialbatch',
name='count',
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='存量'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.6 on 2021-11-11 01:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('inm', '0014_auto_20211111_0918'),
]
operations = [
migrations.RemoveField(
model_name='fifo',
name='operator',
),
migrations.AddField(
model_name='fifo',
name='auditor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='审核人'),
),
]

View File

@ -7,6 +7,7 @@ from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.pm.models import SubProductionPlan from apps.pm.models import SubProductionPlan
from django.core.validators import MinValueValidator, MaxValueValidator
class WareHouse(CommonAModel): class WareHouse(CommonAModel):
@ -28,7 +29,7 @@ class Inventory(BaseModel):
库存物料 库存物料
""" """
material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息') material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息')
count = models.IntegerField('仓库物料存量', default=0) count = models.IntegerField('仓库物料存量', default=0, validators=[MinValueValidator(0)])
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
class Meta: class Meta:
verbose_name = '库存表' verbose_name = '库存表'
@ -40,7 +41,7 @@ class MaterialBatch(BaseModel):
""" """
material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息') material = models.ForeignKey(Material, on_delete=models.CASCADE, verbose_name='物料信息')
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
count = models.IntegerField('存量', default=0) count = models.IntegerField('存量', default=0, validators=[MinValueValidator(0)])
batch = models.CharField('批次号', max_length=100, default='') batch = models.CharField('批次号', max_length=100, default='')
expiration_date = models.DateField('有效期', null=True, blank=True) expiration_date = models.DateField('有效期', null=True, blank=True)
class Meta: class Meta:
@ -65,8 +66,7 @@ class FIFO(CommonAModel):
) )
type = models.IntegerField('出入库类型', default=1) type = models.IntegerField('出入库类型', default=1)
is_audited = models.BooleanField('是否审核', default=False) is_audited = models.BooleanField('是否审核', default=False)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库') auditor = models.ForeignKey(User, verbose_name='审核人', on_delete=models.CASCADE, null=True, blank=True)
operator = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True)
inout_date = models.DateField('出入库日期') inout_date = models.DateField('出入库日期')
remark = models.CharField('备注', max_length=1000, default='') remark = models.CharField('备注', max_length=1000, default='')
@ -78,8 +78,9 @@ 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)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='仓库')
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
count = models.IntegerField('数量', default=0) count = models.IntegerField('数量', default=0, validators=[MinValueValidator(0)])
batch = models.CharField('批次号', max_length=100, default='') batch = models.CharField('批次号', max_length=100, default='')
fifo = models.ForeignKey(FIFO, verbose_name='关联出入库', on_delete=models.CASCADE) fifo = models.ForeignKey(FIFO, verbose_name='关联出入库', on_delete=models.CASCADE)

View File

@ -1,6 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory from apps.inm.models import FIFO, FIFOItem, IProduct, MaterialBatch, WareHouse,Inventory
from apps.qm.models import TestRecord, TestRecordItem
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
@ -37,13 +38,14 @@ class MaterialBatchSerializer(serializers. ModelSerializer):
fields = '__all__' fields = '__all__'
class FIFOListSerializer(serializers.ModelSerializer): class FIFOListSerializer(serializers.ModelSerializer):
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) auditor_ = UserSimpleSerializer(source='auditor', read_only=True)
operator_ = UserSimpleSerializer(source='operator', read_only=True) create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta: class Meta:
model=FIFO model=FIFO
fields = '__all__' fields = '__all__'
class FIFOItemSerializer(serializers.ModelSerializer): class FIFOItemSerializer(serializers.ModelSerializer):
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
material_= MaterialSimpleSerializer(source='material', read_only=True) material_= MaterialSimpleSerializer(source='material', read_only=True)
class Meta: class Meta:
model= FIFOItem model= FIFOItem
@ -58,7 +60,7 @@ class FIFODetailInPurSerializer(serializers.ModelSerializer):
details = IProductInPurSerializer(many=True, required=False) details = IProductInPurSerializer(many=True, required=False)
class Meta: class Meta:
model = FIFOItem model = FIFOItem
fields = ['material', 'count', 'batch', 'details'] fields = ['material', 'count', 'batch', 'details', 'warehouse']
class MaterialBatchQuerySerializer(serializers.Serializer): class MaterialBatchQuerySerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID", required=False) warehouse = serializers.IntegerField(label="仓库ID", required=False)
@ -72,7 +74,7 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
details = FIFODetailInPurSerializer(many=True) details = FIFODetailInPurSerializer(many=True)
class Meta: class Meta:
model = FIFO model = FIFO
fields = ['warehouse', 'operator', 'details', 'inout_date'] fields = ['details', 'inout_date']
def create(self, validated_data): def create(self, validated_data):
details = validated_data.pop('details') details = validated_data.pop('details')
@ -80,16 +82,16 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
pass pass
else: else:
raise serializers.ValidationError('没有入库内容') raise serializers.ValidationError('没有入库内容')
for i in details: # for i in details:
# 校验批次 # # 校验批次
try: # try:
if i['batch']: # if i['batch']:
obj = MaterialBatch.objects.get(batch=i['batch'], material=i['material']) # obj = MaterialBatch.objects.get(batch=i['batch'], material=i['material'])
if obj.warehouse != validated_data['warehouse']: # if obj.warehouse != validated_data['warehouse']:
raise serializers.ValidationError('批次号{}在其他仓库已存在'.format(i['batch'])) # raise serializers.ValidationError('批次号{}在其他仓库已存在'.format(i['batch']))
except: # except:
pass # pass
# 创建采购入库 # 创建采购入库
with transaction.atomic(): with transaction.atomic():
@ -118,3 +120,16 @@ class FIFOInPurSerializer(serializers.ModelSerializer):
FIFOItem.objects.create(**i) FIFOItem.objects.create(**i)
return obj return obj
class InmTestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value', 'is_testok']
class InmTestRecordCreateSerializer(serializers.ModelSerializer):
record_data = InmTestRecordItemCreateSerializer(many=True)
fifo_item = serializers.PrimaryKeyRelatedField(queryset=FIFOItem.objects.all(), required=True)
class Meta:
model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'fifo_item']

View File

@ -8,11 +8,11 @@ def update_inm(instance:FIFO, type:int=1):
""" """
更新库存(正反) 更新库存(正反)
""" """
warehouse = instance.warehouse
if instance.type in [FIFO.FIFO_TYPE_PUR_IN]: # 采购入库 if instance.type in [FIFO.FIFO_TYPE_PUR_IN]: # 采购入库
# 更新相关表 # 更新相关表
for i in FIFOItem.objects.filter(fifo=instance): for i in FIFOItem.objects.filter(fifo=instance):
material = i.material material = i.material
warehouse = i.warehouse
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})
o1.count = o1.count + i.count o1.count = o1.count + i.count
@ -27,6 +27,7 @@ def update_inm(instance:FIFO, type:int=1):
# 更新相关表 # 更新相关表
for i in FIFOItem.objects.filter(fifo=instance): for i in FIFOItem.objects.filter(fifo=instance):
material = i.material material = i.material
warehouse = i.warehouse
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
o1.save() o1.save()

View File

@ -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', FIFOItemViewSet, basename='fifodetail') router.register('fifoitem', FIFOItemViewSet, basename='fifoitem')
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -6,8 +6,9 @@ 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, FIFOItem, MaterialBatch, WareHouse,Inventory from apps.inm.models import FIFO, FIFOItem, MaterialBatch, WareHouse,Inventory
from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer from apps.inm.serializers import FIFOItemSerializer, FIFOInPurSerializer, FIFOListSerializer, InmTestRecordCreateSerializer, MaterialBatchQuerySerializer, MaterialBatchSerializer, WareHouseSerializer, WareHouseCreateUpdateSerializer,InventorySerializer
from apps.inm.signals import update_inm from apps.inm.signals import update_inm
from apps.qm.models import TestRecordItem
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
@ -81,16 +82,50 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
raise APIException('该出入库记录已通过审核, 无法删除') raise APIException('该出入库记录已通过审核, 无法删除')
return super().perform_destroy(instance) return super().perform_destroy(instance)
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=InmTestRecordCreateSerializer)
def test(self, request, pk=None):
"""
检测
"""
serializer = InmTestRecordCreateSerializer(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)
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_item:
obj.fifo_item.is_testok = True if obj.is_testok else False
obj.fifo_item.is_tested = True
obj.fifo_item.save()
return Response()
class FIFOViewSet(ListModelMixin, GenericViewSet): class FIFOViewSet(ListModelMixin, GenericViewSet):
""" """
出入库记录 出入库记录
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = FIFO.objects.select_related('warehouse', 'operator') queryset = FIFO.objects.select_related('auditor', 'create_by')
serializer_class = FIFOListSerializer serializer_class = FIFOListSerializer
filterset_fields = '__all__' filterset_fields = '__all__'
ordering_fields = '__all__' ordering_fields = '__all__'
search_fields = ['warehouse__name', 'warehouse__number'] search_fields = []
ordering = ['-pk'] ordering = ['-pk']
def get_serializer_class(self): def get_serializer_class(self):
@ -121,6 +156,7 @@ class FIFOViewSet(ListModelMixin, GenericViewSet):
raise APIException('该入库记录已审核通过') raise APIException('该入库记录已审核通过')
with transaction.atomic(): with transaction.atomic():
obj.is_audited = True obj.is_audited = True
obj.auditor = request.user
obj.save() obj.save()
update_inm(obj) # 更新库存 update_inm(obj) # 更新库存
return Response() return Response()

View File

@ -171,7 +171,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
materials = [] materials = []
for i in need: for i in need:
materials.append(i['material']) materials.append(i['material'])
objs = MaterialBatch.objects.filter(warehouse=request.data['warehouse'], material__id__in=materials) objs = MaterialBatch.objects.filter(material__id__in=materials)
have = MaterialBatchSerializer(instance=objs, many=True).data have = MaterialBatchSerializer(instance=objs, many=True).data
return Response({'need':need, 'have':have}) return Response({'need':need, 'have':have})

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.6 on 2021-11-11 06:05
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0029_step_type'),
('wpm', '0012_auto_20211111_1056'),
('qm', '0007_alter_testrecorditem_field_type'),
]
operations = [
migrations.AddField(
model_name='testrecord',
name='m_state',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联的物料状态'),
),
migrations.AddField(
model_name='testrecord',
name='remark',
field=models.TextField(default='', verbose_name='备注'),
),
migrations.AddField(
model_name='testrecord',
name='wproduct',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='关联的动态产品'),
),
]

View File

@ -49,7 +49,10 @@ 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)
is_testok = models.BooleanField('是否合格', default=True) is_testok = models.BooleanField('是否合格', default=True)
wproduct = models.ForeignKey('wpm.wproduct', verbose_name='关联的动态产品', on_delete=models.CASCADE, null=True, blank=True)
m_state = models.ForeignKey('mtm.material', 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) fifo_item = models.ForeignKey('inm.fifoitem', verbose_name='关联的出入库批次', on_delete=models.CASCADE, null=True, blank=True)
remark = models.TextField('备注', default='')
class TestRecordItem(BaseModel): class TestRecordItem(BaseModel):

View File

@ -1,7 +1,8 @@
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
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, TestRecordItem 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 GenericViewSet, ModelViewSet
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.response import Response from rest_framework.response import Response
@ -42,7 +43,7 @@ class TestItemViewSet(CreateUpdateModelAMixin, ModelViewSet):
return TestItemCreateUpdateSerializer return TestItemCreateUpdateSerializer
return TestItemSerializer return TestItemSerializer
class TestRecordViewSet(ModelViewSet): class TestRecordViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
""" """
检测记录 检测记录
""" """
@ -52,41 +53,39 @@ class TestRecordViewSet(ModelViewSet):
ordering = ['-id'] ordering = ['-id']
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'create': if self.action == 'list':
return TestRecordCreateSerializer
elif self.action == 'list':
return TestRecordListSerializer return TestRecordListSerializer
elif self.action == 'retrieve': elif self.action == 'retrieve':
return TestRecordDetailSerializer return TestRecordDetailSerializer
return super().get_serializer_class() return super().get_serializer_class()
def create(self, request, *args, **kwargs): # def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) # serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) # serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data # vdata = serializer.validated_data
record_data = vdata.pop('record_data') # record_data = vdata.pop('record_data')
if 'is_testok' not in vdata: # if 'is_testok' not in vdata:
raise APIException('未填写检测结论') # raise APIException('未填写检测结论')
with transaction.atomic(): # with transaction.atomic():
obj = serializer.save(create_by = self.request.user) # obj = serializer.save(create_by = self.request.user)
tris = [] # tris = []
for m in record_data: # 保存记录详情 # for m in record_data: # 保存记录详情
form_field = m['form_field'] # form_field = m['form_field']
m['field_name'] = form_field.field_name # m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key # m['field_key'] = form_field.field_key
m['field_type'] = form_field.field_type # m['field_type'] = form_field.field_type
m['field_value'] = m['field_value'] # m['field_value'] = m['field_value']
m['sort'] = form_field.sort # m['sort'] = form_field.sort
m['need_judge'] = form_field.need_judge # m['need_judge'] = form_field.need_judge
m['is_testok'] = m['is_testok'] if 'is_testok' in m else True # m['is_testok'] = m['is_testok'] if 'is_testok' in m else True
m['test_record'] = obj # m['test_record'] = obj
tris.append(TestRecordItem(**m)) # tris.append(TestRecordItem(**m))
TestRecordItem.objects.bulk_create(tris) # TestRecordItem.objects.bulk_create(tris)
# 如果检测合格 # # 如果检测合格
if obj.fifo_item: # if obj.fifo_item:
obj.fifo_item.is_testok = True if obj.is_testok else False # obj.fifo_item.is_testok = True if obj.is_testok else False
obj.fifo_item.is_tested = True # obj.fifo_item.is_tested = True
obj.fifo_item.save() # obj.fifo_item.save()
return Response(status=status.HTTP_201_CREATED) # return Response(status=status.HTTP_201_CREATED)

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.6 on 2021-11-11 02:56
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0029_step_type'),
('wpm', '0011_alter_operationrecorditem_operation_record'),
]
operations = [
migrations.AddField(
model_name='operation',
name='use_scrap',
field=models.BooleanField(default=False, verbose_name='是否使用的边角料'),
),
migrations.AddField(
model_name='wproduct',
name='pre_pstate',
field=models.ForeignKey(blank=True, help_text='已执行完的步骤', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='w_pre_pstate', to='mtm.step', verbose_name='已执行到'),
),
migrations.AlterField(
model_name='operationmaterial',
name='count',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(0)], verbose_name='消耗或产出数量'),
),
migrations.AlterField(
model_name='wmaterial',
name='count',
field=models.IntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='当前数量'),
),
migrations.AlterField(
model_name='wproduct',
name='p_state',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='w_ptate', to='mtm.step', verbose_name='所在步骤'),
),
]

View File

@ -7,7 +7,7 @@ from apps.system.models import CommonAModel, CommonBModel, Organization, User, D
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, RecordFormField, Step, RecordForm from apps.mtm.models import Material, Process, RecordFormField, Step, RecordForm
from django.core.validators import MinValueValidator
class WMaterial(BaseModel): class WMaterial(BaseModel):
""" """
车间生产物料 车间生产物料
@ -15,7 +15,7 @@ class WMaterial(BaseModel):
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子计划', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, 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, validators=[MinValueValidator(0)])
class WProduct(CommonAModel): class WProduct(CommonAModel):
""" """
@ -31,7 +31,8 @@ class WProduct(CommonAModel):
) )
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) pre_pstate = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_pstate')
p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='w_ptate')
act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices) act_state = models.IntegerField('进行状态', default=0, choices=act_state_choices)
is_executed = models.BooleanField('子工序是否已执行', default=False) is_executed = models.BooleanField('子工序是否已执行', default=False)
is_hidden = models.BooleanField('是否隐藏', default=False) is_hidden = models.BooleanField('是否隐藏', default=False)
@ -48,11 +49,12 @@ class Operation(CommonAModel):
wproducts = models.JSONField('关联产品ID列表', default=list, blank=True) wproducts = models.JSONField('关联产品ID列表', default=list, blank=True)
m_state = models.ForeignKey(Material, verbose_name='操作时的物料状态', on_delete=models.CASCADE, null=True, 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) p_state = models.ForeignKey(Step, verbose_name='操作步骤', on_delete=models.CASCADE, null=True, blank=True)
use_scrap = models.BooleanField('是否使用的边角料', default=False)
remark = models.CharField('操作备注', max_length=200, null=True, blank=True) remark = models.CharField('操作备注', max_length=200, null=True, blank=True)
class OperationMaterial(BaseModel): class OperationMaterial(BaseModel):
""" """
车间生产物料消耗产出表 生产操作物料消耗产出表
""" """
type_choices=( type_choices=(
(1, '消耗'), (1, '消耗'),
@ -62,7 +64,7 @@ class OperationMaterial(BaseModel):
operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE) operation = models.ForeignKey(Operation, verbose_name='关联的生产操作', on_delete=models.CASCADE)
wmaterial = models.ForeignKey(WMaterial, verbose_name='关联的车间物料', on_delete=models.CASCADE, null=True, blank=True) 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) material = models.ForeignKey(Material, verbose_name='可能产出的副产品', on_delete=models.CASCADE, null=True, blank=True)
count = models.IntegerField('消耗或产出数量') count = models.IntegerField('消耗或产出数量', validators=[MinValueValidator(0)])
class OperationRecord(CommonAModel): class OperationRecord(CommonAModel):
""" """

View File

@ -1,4 +1,4 @@
from rest_framework import serializers from rest_framework import serializers, exceptions
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from apps.inm.models import FIFO, FIFOItem, 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
@ -8,6 +8,7 @@ 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 django.utils.translation import gettext_lazy as _
from apps.qm.models import TestRecord, TestRecordItem
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem from apps.wpm.models import Operation, WMaterial, WProduct, OperationRecord, OperationRecordItem
from django.db import transaction from django.db import transaction
@ -15,32 +16,30 @@ 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")
batch = serializers.CharField(label='物料批次', allow_blank=True) batch = serializers.CharField(label='物料批次', allow_blank=True)
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
pick_count = serializers.IntegerField(label="领料数量") pick_count = serializers.IntegerField(label="领料数量")
class PickSerializer(serializers.Serializer): class PickSerializer(serializers.Serializer):
subproduction_plan=serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID") subproduction_plan=serializers.PrimaryKeyRelatedField(queryset=SubProductionPlan.objects.all(), label="子计划ID")
warehouse = serializers.PrimaryKeyRelatedField(queryset=WareHouse.objects.all(), label="仓库ID")
picks = PickDetailSerializer(many=True) picks = PickDetailSerializer(many=True)
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]: if sp.state not in [1,2]:
raise serializers.ValidationError('该子计划状态错误') raise exceptions.ValidationError('该子计划状态错误')
if sp.is_picked: if sp.is_picked:
raise serializers.ValidationError('该子计划已领料') raise exceptions.ValidationError('该子计划已领料')
for i in picks: # for i in picks:
try: # try:
instance = MaterialBatch.objects.get(material=i['material'], batch=i['batch']) # instance = MaterialBatch.objects.get(material=i['material'], batch=i['batch'])
if instance.count < i['pick_count']: # if instance.count < i['pick_count']:
raise serializers.ValidationError('物料不足') # raise exceptions.ValidationError('物料不足')
except: # except:
raise serializers.ValidationError('物料不存在') # raise exceptions.ValidationError('物料不存在')
# 创建出库记录 # 创建出库记录
with transaction.atomic(): with transaction.atomic():
operator = self.context['request'].user validated_data['create_by'] = self.context['request'].user
validated_data['create_by'] = operator
validated_data['operator'] = operator
validated_data['type'] = FIFO.FIFO_TYPE_DO_OUT 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)
@ -126,21 +125,21 @@ class OperationInitSerializer(serializers.Serializer):
stepIds=[i['id'] for i in subproduction_plan.steps] stepIds=[i['id'] for i in subproduction_plan.steps]
if step.id not in stepIds: if step.id not in stepIds:
raise serializers.ValidationError('请选择正确的子工序操作') raise exceptions.ValidationError('请选择正确的子工序操作')
if 'wproducts' in data and data['wproducts']: if 'wproducts' in data and data['wproducts']:
if step.type == Step.STEP_TYPE_DIV: if step.type == Step.STEP_TYPE_DIV:
raise serializers.ValidationError(_('不可进行此操作')) raise exceptions.ValidationError(_('不可进行此操作'))
for i in data['wproducts']: for i in data['wproducts']:
if i.is_executed: if i.is_executed:
raise serializers.ValidationError('不可进行操作') raise exceptions.ValidationError('不可进行操作')
if i.subproduction_plan != subproduction_plan: if i.subproduction_plan != subproduction_plan:
raise serializers.ValidationError('半成品所属子计划不一致') raise exceptions.ValidationError('半成品所属子计划不一致')
if i.p_state != step: if i.p_state != step:
raise serializers.ValidationError('半成品所属子工序不一致') raise exceptions.ValidationError('半成品所属子工序不一致')
else: else:
if step.type != Step.STEP_TYPE_DIV: if step.type != Step.STEP_TYPE_DIV:
raise serializers.ValidationError(_('请选择半成品进行操作')) raise exceptions.ValidationError(_('请选择半成品进行操作'))
return data return data
@ -174,6 +173,19 @@ class OperationSubmitSerializer(serializers.Serializer):
output = DoOutputSerializer(many=True, required=False) output = DoOutputSerializer(many=True, required=False)
forms = OperationRecordSerializer(many=True, required=False) forms = OperationRecordSerializer(many=True, required=False)
remark = serializers.CharField(required=False, label='操作备注', allow_blank=True, allow_null=True) remark = serializers.CharField(required=False, label='操作备注', allow_blank=True, allow_null=True)
use_scrap = serializers.BooleanField(required=False, default=False)
class WpmTestRecordItemCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TestRecordItem
fields = ['form_field', 'field_value', 'is_testok']
class WpmTestRecordCreateSerializer(serializers.ModelSerializer):
record_data = WpmTestRecordItemCreateSerializer(many=True)
wproduct = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), required=True)
class Meta:
model = TestRecord
fields = ['form', 'record_data', 'is_testok', 'wproduct']

View File

@ -9,14 +9,16 @@ from apps.mtm.models import Material, RecordForm, Step, SubprodctionMaterial
from apps.mtm.serializers import RecordFormDetailSerializer from apps.mtm.serializers import RecordFormDetailSerializer
from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
from apps.qm.models import TestRecordItem
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, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem from apps.wpm.models import WMaterial, WProduct, Operation, OperationMaterial, OperationRecord, OperationRecordItem
from apps.wpm.serializers import OperationDetailSerializer, OperationListSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer from apps.wpm.serializers import OperationDetailSerializer, OperationListSerializer, PickSerializer, OperationInitSerializer, OperationSubmitSerializer, WMaterialListSerializer, WProductListSerializer, WpmTestRecordCreateSerializer
from rest_framework.response import Response from rest_framework.response import Response
from django.db import transaction from django.db import transaction
from rest_framework import exceptions
# Create your views here. # Create your views here.
class WPlanViewSet(ListModelMixin, GenericViewSet): class WPlanViewSet(ListModelMixin, GenericViewSet):
""" """
@ -63,9 +65,40 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
ordering_fields = ['id'] ordering_fields = ['id']
ordering = ['id'] ordering = ['id']
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=PickSerializer) @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestRecordCreateSerializer)
def test(): @transaction.atomic
pass def test(self, request, pk=None):
"""
检测
"""
serializer = WpmTestRecordCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
record_data = vdata.pop('record_data')
wproduct = vdata['wproduct']
if wproduct.act_state != WProduct.WPR_ACT_STATE_TOTEST:
raise exceptions.APIException('该半成品无需检测')
if 'is_testok' not in vdata:
raise exceptions.APIException('未填写检测结论')
obj = serializer.save(create_by = self.request.user, m_state=vdata['wproduct'].m_state)
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)
# 如果检测合格
return Response()
class OperationViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): class OperationViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
""" """
@ -151,6 +184,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
action_obj.m_state = vdata['wproducts'][0].m_state action_obj.m_state = vdata['wproducts'][0].m_state
action_obj.remark = vdata.get('remark', '') # 操作备注 action_obj.remark = vdata.get('remark', '') # 操作备注
action_obj.create_by = request.user action_obj.create_by = request.user
action_obj.use_scrap = vdata.get('use_scrap', False)
action_obj.save() action_obj.save()
# 保存物料消耗 # 保存物料消耗
@ -207,7 +241,7 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
pindex = stepIds.index(vdata['step'].id) pindex = stepIds.index(vdata['step'].id)
if pindex + 1 < len(stepIds): # 如果不是最后一步 if pindex + 1 < len(stepIds): # 如果不是最后一步
newstep = Step.objects.get(pk=stepIds[pindex+1]) newstep = Step.objects.get(pk=stepIds[pindex+1])
wproducts.update(p_state=newstep, is_executed=False) wproducts.update(p_state=newstep, is_executed=False, pre_pstate=vdata['step'])
# 特殊情况如果是夹层结合 # 特殊情况如果是夹层结合
if vdata['step'].type == Step.STEP_TYPE_COMB: if vdata['step'].type == Step.STEP_TYPE_COMB:
@ -221,9 +255,10 @@ class DoFormSubmit(CreateAPIView, GenericAPIView):
parent = data['wproducts'] parent = data['wproducts']
) )
else: # 如果是最后一步, 此时需要转序并更新状态为待检测 else: # 如果是最后一步, 此时需要转序并更新状态为待检测, 此时物料状态需变成当前子计划的主产物状态
newstep = vdata['step'] newstep = vdata['step']
wproducts.update(p_state=newstep, is_executed=True, act_state=WProduct.WPR_ACT_STATE_TOTEST) wproducts.update(p_state=newstep, is_executed=True,
act_state=WProduct.WPR_ACT_STATE_TOTEST, pre_pstate=newstep, m_state=vdata['subproduction_plan'].main_product)
# 特殊情况如果是夹层结合 # 特殊情况如果是夹层结合
if vdata['step'].type == Step.STEP_TYPE_COMB: if vdata['step'].type == Step.STEP_TYPE_COMB: