Support manual route output materials
This commit is contained in:
parent
d5d940c5e4
commit
bcbb27374f
|
|
@ -3,11 +3,12 @@ from apps.mtm.models import (Shift, Material, Mgroup, Team, Goal,
|
||||||
Process, Route, TeamMember, RoutePack, Srule, RouteMat)
|
Process, Route, TeamMember, RoutePack, Srule, RouteMat)
|
||||||
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS_DEPT
|
from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE, EXCLUDE_FIELDS_DEPT
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError, ParseError
|
from rest_framework.exceptions import ValidationError, ParseError, PermissionDenied
|
||||||
from apps.system.models import Dept, UserPost
|
from apps.system.models import Dept, UserPost
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from apps.wf.serializers import TicketSimpleSerializer
|
from apps.wf.serializers import TicketSimpleSerializer
|
||||||
from apps.system.serializers import FileSerializer
|
from apps.system.serializers import FileSerializer
|
||||||
|
from apps.utils.permission import has_perm
|
||||||
|
|
||||||
|
|
||||||
class ShiftSerializer(CustomModelSerializer):
|
class ShiftSerializer(CustomModelSerializer):
|
||||||
|
|
@ -177,6 +178,23 @@ class RoutePackCopySerializer(serializers.Serializer):
|
||||||
material_in = serializers.CharField(label='原料ID')
|
material_in = serializers.CharField(label='原料ID')
|
||||||
material_out = serializers.CharField(label='产品ID')
|
material_out = serializers.CharField(label='产品ID')
|
||||||
|
|
||||||
|
|
||||||
|
class RouteMaterialOutCreateSerializer(serializers.Serializer):
|
||||||
|
"""工艺路线内快捷新建半成品时允许录入的物料字段。"""
|
||||||
|
name = serializers.CharField(label='名称', max_length=50)
|
||||||
|
number = serializers.CharField(label='编号', max_length=100, required=False, allow_blank=True, allow_null=True)
|
||||||
|
cate = serializers.CharField(label='大类', max_length=20, required=False, allow_blank=True)
|
||||||
|
model = serializers.CharField(label='型号', max_length=100, required=False, allow_blank=True, allow_null=True)
|
||||||
|
specification = serializers.CharField(label='规格', max_length=100, required=False, allow_blank=True, allow_null=True)
|
||||||
|
unit = serializers.CharField(label='基准计量单位', max_length=10, required=False, default='个')
|
||||||
|
test_mode_in = serializers.ChoiceField(
|
||||||
|
label='入库检验方式',
|
||||||
|
choices=(Material.MA_TEST_EXEMPT, Material.MA_TEST_REQUIRED),
|
||||||
|
required=False,
|
||||||
|
default=Material.MA_TEST_REQUIRED,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RouteSerializer(CustomModelSerializer):
|
class RouteSerializer(CustomModelSerializer):
|
||||||
name = serializers.CharField(source='__str__', read_only=True)
|
name = serializers.CharField(source='__str__', read_only=True)
|
||||||
material_ = MaterialSerializer(source='material', read_only=True)
|
material_ = MaterialSerializer(source='material', read_only=True)
|
||||||
|
|
@ -194,6 +212,7 @@ class RouteSerializer(CustomModelSerializer):
|
||||||
material_out_is_hidden = serializers.BooleanField(
|
material_out_is_hidden = serializers.BooleanField(
|
||||||
source='material_out.is_hidden', read_only=True)
|
source='material_out.is_hidden', read_only=True)
|
||||||
material_out_tracking = serializers.IntegerField(write_only=True, required=False, allow_null=True)
|
material_out_tracking = serializers.IntegerField(write_only=True, required=False, allow_null=True)
|
||||||
|
material_out_create = RouteMaterialOutCreateSerializer(write_only=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Route
|
model = Route
|
||||||
|
|
@ -201,6 +220,8 @@ class RouteSerializer(CustomModelSerializer):
|
||||||
read_only_fields = EXCLUDE_FIELDS
|
read_only_fields = EXCLUDE_FIELDS
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
if attrs.get('material_out') and attrs.get('material_out_create'):
|
||||||
|
raise ParseError('选择已有输出物料和新建半成品不可同时提交')
|
||||||
if attrs.get('routepack', None):
|
if attrs.get('routepack', None):
|
||||||
attrs['material'] = attrs['routepack'].material
|
attrs['material'] = attrs['routepack'].material
|
||||||
if 'mgroup' in attrs and attrs['mgroup']:
|
if 'mgroup' in attrs and attrs['mgroup']:
|
||||||
|
|
@ -262,12 +283,30 @@ class RouteSerializer(CustomModelSerializer):
|
||||||
})
|
})
|
||||||
return material_out
|
return material_out
|
||||||
|
|
||||||
|
def create_material_out(self, material_data, process, tracking):
|
||||||
|
if self.request and not has_perm(self.request.user, ['material.create']):
|
||||||
|
raise PermissionDenied('无新建物料权限')
|
||||||
|
material_data.update({
|
||||||
|
'type': Material.MA_TYPE_HALFGOOD,
|
||||||
|
'process': process.id,
|
||||||
|
'tracking': tracking,
|
||||||
|
'is_hidden': False,
|
||||||
|
})
|
||||||
|
serializer = MaterialSerializer(data=material_data, context=self.context)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
return serializer.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
process = validated_data['process']
|
process = validated_data['process']
|
||||||
routepack = validated_data.get('routepack', None)
|
routepack = validated_data.get('routepack', None)
|
||||||
|
material_out_create = validated_data.pop('material_out_create', None)
|
||||||
material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH)
|
material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH)
|
||||||
if material_out_tracking is None:
|
if material_out_tracking is None:
|
||||||
material_out_tracking = Material.MA_TRACKING_BATCH
|
material_out_tracking = Material.MA_TRACKING_BATCH
|
||||||
|
if material_out_create:
|
||||||
|
validated_data['material_out'] = self.create_material_out(
|
||||||
|
material_out_create, process, material_out_tracking)
|
||||||
if routepack:
|
if routepack:
|
||||||
pass
|
pass
|
||||||
# if Route.objects.filter(routepack=routepack, process=process).exists():
|
# if Route.objects.filter(routepack=routepack, process=process).exists():
|
||||||
|
|
@ -308,12 +347,17 @@ class RouteSerializer(CustomModelSerializer):
|
||||||
# raise ParseError(f"该工艺步骤已存在-{msg}")
|
# raise ParseError(f"该工艺步骤已存在-{msg}")
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
validated_data.pop('material', None)
|
validated_data.pop('material', None)
|
||||||
process = validated_data.pop('process', None)
|
process = validated_data.pop('process', None) or instance.process
|
||||||
|
material_out_create = validated_data.pop('material_out_create', None)
|
||||||
material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH)
|
material_out_tracking = validated_data.pop("material_out_tracking", Material.MA_TRACKING_BATCH)
|
||||||
if material_out_tracking is None:
|
if material_out_tracking is None:
|
||||||
material_out_tracking = Material.MA_TRACKING_BATCH
|
material_out_tracking = Material.MA_TRACKING_BATCH
|
||||||
|
if material_out_create:
|
||||||
|
validated_data['material_out'] = self.create_material_out(
|
||||||
|
material_out_create, process, material_out_tracking)
|
||||||
|
|
||||||
if not validated_data.get('material_out', instance.material_out) and not instance.material:
|
if not validated_data.get('material_out', instance.material_out) and not instance.material:
|
||||||
raise ParseError('未指定输出物料,且工艺包未设置产品无法自动生成;请指定输出物料,或先在工艺包基本信息中选择产品')
|
raise ParseError('未指定输出物料,且工艺包未设置产品无法自动生成;请指定输出物料,或先在工艺包基本信息中选择产品')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,69 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
from apps.mtm.models import Material, Process, Route, RoutePack
|
||||||
|
from apps.mtm.serializers import RouteSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class RouteMaterialOutCreateSerializerTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.process = Process.objects.create(name='切割')
|
||||||
|
self.product = Material.objects.create(
|
||||||
|
name='产品A',
|
||||||
|
number='CP-A',
|
||||||
|
type=Material.MA_TYPE_GOOD,
|
||||||
|
process=self.process,
|
||||||
|
)
|
||||||
|
self.material_in = Material.objects.create(
|
||||||
|
name='原料A',
|
||||||
|
number='YL-A',
|
||||||
|
type=Material.MA_TYPE_MAINSO,
|
||||||
|
)
|
||||||
|
self.routepack = RoutePack.objects.create(name='产品A工艺', material=self.product)
|
||||||
|
|
||||||
|
def test_create_route_and_visible_halfgood_in_one_serializer_save(self):
|
||||||
|
serializer = RouteSerializer(data={
|
||||||
|
'routepack': self.routepack.id,
|
||||||
|
'process': self.process.id,
|
||||||
|
'material_in': self.material_in.id,
|
||||||
|
'material_out_tracking': Material.MA_TRACKING_SINGLE,
|
||||||
|
'material_out_create': {
|
||||||
|
'name': '分支半成品A',
|
||||||
|
'number': 'BCP-A',
|
||||||
|
'specification': 'S1',
|
||||||
|
'model': 'M1',
|
||||||
|
'unit': '件',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
route = serializer.save()
|
||||||
|
|
||||||
|
material_out = route.material_out
|
||||||
|
self.assertEqual(material_out.type, Material.MA_TYPE_HALFGOOD)
|
||||||
|
self.assertEqual(material_out.process, self.process)
|
||||||
|
self.assertEqual(material_out.tracking, Material.MA_TRACKING_SINGLE)
|
||||||
|
self.assertFalse(material_out.is_hidden)
|
||||||
|
self.assertEqual(material_out.name, '分支半成品A')
|
||||||
|
self.assertEqual(material_out.unit, '件')
|
||||||
|
self.assertTrue(Route.objects.filter(pk=route.pk, material_out=material_out).exists())
|
||||||
|
|
||||||
|
def test_existing_and_new_material_out_are_mutually_exclusive(self):
|
||||||
|
existing = Material.objects.create(
|
||||||
|
name='已有半成品',
|
||||||
|
number='BCP-OLD',
|
||||||
|
type=Material.MA_TYPE_HALFGOOD,
|
||||||
|
process=self.process,
|
||||||
|
)
|
||||||
|
serializer = RouteSerializer(data={
|
||||||
|
'routepack': self.routepack.id,
|
||||||
|
'process': self.process.id,
|
||||||
|
'material_in': self.material_in.id,
|
||||||
|
'material_out': existing.id,
|
||||||
|
'material_out_create': {
|
||||||
|
'name': '新半成品',
|
||||||
|
'number': 'BCP-NEW',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertFalse(serializer.is_valid())
|
||||||
|
self.assertIn('non_field_errors', serializer.errors)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue