1
0
forked from erp-dev/erp
Files
erpnew/api_v1/views/printing/test_plate_order_api.py

1195 lines
48 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
PlateOrder API 测试
"""
import json
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework.test import APIClient
from rest_framework import status
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from basic_info import models as basic_models
from printing import models as printing_models
from stateflow import models as stateflow_models
from api_v1 import models as api_models
User = get_user_model()
class PlateOrderAPITestCase(TestCase):
"""测试 PlateOrder API"""
def setUp(self):
self.client = APIClient()
# 创建商户
self.merchant = basic_models.Merchant.objects.create(
name='测试商户',
type=basic_models.MerchantTypeEnum.FACTORY
)
# 创建用户
self.user = User.objects.create_user(
username='testuser',
password='testpass123',
email='test@example.com'
)
# 创建员工并关联商户
self.employee = basic_models.Employee.objects.create(
sys_user=self.user,
merchant=self.merchant,
name='测试员工',
mobile='13800138000',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建业务员
self.salesperson = basic_models.Employee.objects.create(
merchant=self.merchant,
name='测试业务员',
mobile='13800138001',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建跟单员
self.merchandiser = basic_models.Employee.objects.create(
merchant=self.merchant,
name='测试跟单员',
mobile='13800138002',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建设计师
self.designer = basic_models.Employee.objects.create(
merchant=self.merchant,
name='测试设计师',
mobile='13800138003',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建客户
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='测试客户',
mobile='13900139000',
area='测试地区'
)
# 认证用户
self.client.force_authenticate(user=self.user)
# 给用户添加基础权限
view_perm = Permission.objects.get(codename='view_plateorder')
add_perm = Permission.objects.get(codename='add_plateorder')
change_perm = Permission.objects.get(codename='change_plateorder')
delete_perm = Permission.objects.get(codename='delete_plateorder')
self.user.user_permissions.add(view_perm, add_perm, change_perm, delete_perm)
# 创建测试流程
self.state1 = stateflow_models.State.objects.create(name='画图', description='画图阶段')
self.state2 = stateflow_models.State.objects.create(name='调色', description='调色阶段')
self.state3 = stateflow_models.State.objects.create(name='套样', description='套样阶段')
self.process = stateflow_models.Process.objects.create(
name='开版流程',
description='标准开版流程'
)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state2, order=1)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state3, order=2)
self.upload_file_primary = self._create_uploaded_file('plate-main.jpg')
self.upload_file_secondary = self._create_uploaded_file('plate-secondary.jpg')
def _create_uploaded_file(self, filename):
file = SimpleUploadedFile(filename, b'test-image-content', content_type='image/jpeg')
return api_models.UploadedFile.objects.create(
owner=self.user,
path=file,
original_filename=filename,
file_size=file.size,
content_type='image/jpeg',
)
def test_create_plate_order(self):
"""测试创建开版订单"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN001',
'plate_type': '圆网',
'style_name': '测试款式',
'fabric': '棉布',
'width': '150cm',
'urgency_level': '加急',
'salesperson': self.salesperson.id,
'merchandiser': self.merchandiser.id,
'image_name': 'sample.png',
'plate_image': [
{'file_id': self.upload_file_primary.id, 'name': '主图'}
],
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['design_code'], 'DESIGN001')
self.assertEqual(response.data['style_name'], '测试款式')
self.assertEqual(response.data['image_name'], 'sample.png')
self.assertEqual(len(response.data['plate_image']), 1)
self.assertEqual(response.data['plate_image'][0]['file_id'], self.upload_file_primary.id)
self.assertIn('created_by', response.data)
self.assertEqual(response.data['created_by'], self.user.id)
# 验证数据库中创建了记录
self.assertTrue(printing_models.PlateOrder.objects.filter(design_code='DESIGN001').exists())
created = printing_models.PlateOrder.objects.get(design_code='DESIGN001')
self.assertEqual(created.created_by_id, self.user.id)
def test_create_plate_order_without_customer(self):
"""测试创建开版订单时未提供客户"""
data = {
'design_code': 'DESIGN002',
'plate_type': '圆网',
'style_name': '测试款式2',
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('customer', response.data)
def test_create_plate_order_with_invalid_rating(self):
"""测试创建开版订单时提供无效评级"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN003',
'plate_type': '圆网',
'style_name': '测试款式3',
'drawing_rating': '不存在的评级', # 任意字符串都可以,因为是 CharField
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
# CharField 不会验证内容,所以应该成功
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn('created_by', response.data)
self.assertEqual(response.data['created_by'], self.user.id)
def test_create_plate_order_with_invalid_plate_image_file(self):
data = {
'customer': self.customer.id,
'design_code': 'DESIGN_IMG',
'plate_type': '圆网',
'plate_image': [{'file_id': 999}],
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('plate_image', response.data)
def test_create_plate_order_with_plate_image_json_string_in_multipart(self):
"""兼容前端使用 multipart/form-data 时 plate_image 以 JSON 字符串传入"""
data = {
'customer': str(self.customer.id),
'design_code': 'DESIGN_MULTIPART_IMG',
'plate_type': '圆网',
'plate_image': json.dumps([{'file_id': self.upload_file_primary.id, 'name': '主图'}]),
}
response = self.client.post('/api/v1/plate-orders/', data, format='multipart')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.data)
self.assertIn('plate_image', response.data)
self.assertEqual(len(response.data['plate_image']), 1)
self.assertEqual(response.data['plate_image'][0]['file_id'], self.upload_file_primary.id)
def test_create_plate_order_can_reference_uploaded_file_owned_by_other_user(self):
"""克隆/协作场景:允许引用非本人上传的 UploadedFile只要文件存在且未删除"""
other_user = User.objects.create_user(
username='otheruser',
password='pass123',
email='other@example.com',
)
other_file = api_models.UploadedFile.objects.create(
owner=other_user,
path=SimpleUploadedFile('other.jpg', b'test-image', content_type='image/jpeg'),
original_filename='other.jpg',
file_size=9,
content_type='image/jpeg',
)
data = {
'customer': self.customer.id,
'design_code': 'DESIGN_OTHER_OWNER_IMG',
'plate_type': '圆网',
'plate_image': [{'file_id': other_file.id, 'name': '协作图片'}],
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.data)
self.assertEqual(response.data['plate_image'][0]['file_id'], other_file.id)
def test_list_plate_orders(self):
"""测试获取开版订单列表"""
# 创建测试数据
order_one = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='款式1',
fabric='棉布',
salesperson=self.salesperson,
image_name='first.png',
)
order_one.plate_image = [
{
'file_id': self.upload_file_primary.id,
'name': 'A 面',
'url': self.upload_file_primary.file_url,
'path': self.upload_file_primary.path.name,
}
]
order_one.save(update_fields=['plate_image'])
order_two = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN002',
plate_type='平网',
style_name='款式2',
fabric='涤纶',
merchandiser=self.merchandiser,
image_name='second.png',
)
order_two.plate_image = [
{
'file_id': self.upload_file_secondary.id,
'name': 'B 面',
'url': self.upload_file_secondary.file_url,
'path': self.upload_file_secondary.path.name,
}
]
order_two.save(update_fields=['plate_image'])
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
# 根据实际返回的数据结构调整
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 2)
collection = response.data.get('results') or response.data.get('data') or []
else:
collection = response.data
self.assertEqual(len(collection), 2)
if collection:
self.assertIn('plate_image', collection[0])
self.assertIsInstance(collection[0]['plate_image'], list)
self.assertIn('created_by', collection[0])
def test_retrieve_plate_order(self):
"""测试获取单个开版订单详情"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
urgency_level='紧急',
salesperson=self.salesperson,
merchandiser=self.merchandiser,
image_name='detail.png',
)
plate_order.plate_image = [
{
'file_id': self.upload_file_primary.id,
'name': '详情图',
'url': self.upload_file_primary.file_url,
'path': self.upload_file_primary.path.name,
}
]
plate_order.save(update_fields=['plate_image'])
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['design_code'], 'DESIGN001')
self.assertIn('created_by', response.data)
self.assertIsNone(response.data['created_by'])
self.assertEqual(response.data['urgency_level'], '紧急')
self.assertEqual(response.data['customer'], self.customer.id)
self.assertIn('customer_name', response.data)
self.assertIn('salesperson_name', response.data)
self.assertEqual(response.data['image_name'], 'detail.png')
self.assertIsInstance(response.data['plate_image'], list)
self.assertEqual(response.data['plate_image'][0]['file_id'], self.upload_file_primary.id)
def test_design_code_fallback_in_detail(self):
"""design_code 为空时返回主键ID"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['design_code'], str(plate_order.id))
def test_design_code_fallback_in_list(self):
"""列表接口也应返回主键ID"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='',
plate_type='圆网',
style_name='款式1',
fabric='棉布',
)
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
self.assertEqual(results[0]['design_code'], str(results[0]['id']))
def test_plate_order_list_includes_content_type_id(self):
"""列表接口应包含 content_type_id来源 business_object.content_type_id"""
process = stateflow_models.Process.objects.create(name='P-ct')
ct = ContentType.objects.get_for_model(stateflow_models.Process)
bo = stateflow_models.BusinessObject.objects.create(
name='BO-ct',
process=process,
content_type=ct,
object_id=123,
)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN-CT',
plate_type='圆网',
style_name='款式-ct',
fabric='棉布',
process=process.id,
business_object=bo,
)
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
target = next(item for item in results if item['id'] == plate_order.id)
self.assertIn('content_type_id', target)
self.assertEqual(target['content_type_id'], ct.id)
def test_fabric_source_field_in_detail(self):
"""验证布料来源字段在返回中存在"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN010',
plate_type='圆网',
fabric='棉布',
fabric_source='客户提供',
)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['fabric_source'], '客户提供')
def test_update_plate_order(self):
"""测试更新开版订单"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
)
data = {
'customer': self.customer.id,
'design_code': 'DESIGN001',
'plate_type': '平网', # 修改类型
'style_name': '更新后的款式', # 修改名称
'fabric': '棉布',
'urgency_level': '特急', # 添加紧急程度
}
response = self.client.put(f'/api/v1/plate-orders/{plate_order.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['plate_type'], '平网')
self.assertEqual(response.data['style_name'], '更新后的款式')
self.assertEqual(response.data['urgency_level'], '特急')
def test_partial_update_plate_order(self):
"""测试部分更新开版订单"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
)
data = {
'urgency_level': '加急',
'is_mark_frame': True,
}
response = self.client.patch(f'/api/v1/plate-orders/{plate_order.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['urgency_level'], '加急')
self.assertEqual(response.data['is_mark_frame'], True)
# 其他字段保持不变
self.assertEqual(response.data['design_code'], 'DESIGN001')
def test_delete_plate_order_not_allowed(self):
"""测试删除开版订单(应该被禁止)"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
)
response = self.client.delete(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
# 验证订单仍然存在
self.assertTrue(printing_models.PlateOrder.objects.filter(id=plate_order.id).exists())
def test_filter_by_customer(self):
"""测试按客户筛选"""
customer2 = basic_models.Customer.objects.create(
merchant=self.merchant,
name='客户2',
mobile='13900139001',
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='款式1',
)
printing_models.PlateOrder.objects.create(
customer=customer2,
design_code='DESIGN002',
plate_type='平网',
style_name='款式2',
)
response = self.client.get(f'/api/v1/plate-orders/?customer={self.customer.id}')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['customer'], self.customer.id)
else:
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['customer'], self.customer.id)
def test_filter_by_urgency_level(self):
"""测试按紧急程度筛选"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='款式1',
urgency_level='正常',
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN002',
plate_type='平网',
style_name='款式2',
urgency_level='加急',
)
# urgency_level 是 CharField过滤器是 NumberFilter所以不能过滤
# 改为测试其他过滤器
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 2)
else:
self.assertEqual(len(response.data), 2)
def test_filter_by_is_invalid(self):
"""测试按是否作废筛选"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='款式1',
is_invalid=False,
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN002',
plate_type='平网',
style_name='款式2',
is_invalid=True,
)
response = self.client.get('/api/v1/plate-orders/?is_invalid=false')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['is_invalid'], False)
else:
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['is_invalid'], False)
def test_search_by_design_code(self):
"""测试按设计编号搜索"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='ABC123',
plate_type='圆网',
style_name='款式1',
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='XYZ789',
plate_type='平网',
style_name='款式2',
)
response = self.client.get('/api/v1/plate-orders/?search=ABC')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 1)
self.assertIn('ABC', response.data['results'][0]['design_code'])
else:
self.assertEqual(len(response.data), 1)
self.assertIn('ABC', response.data[0]['design_code'])
def test_search_by_style_name(self):
"""测试按款式名称搜索"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='花朵印染',
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN002',
plate_type='平网',
style_name='条纹印染',
)
response = self.client.get('/api/v1/plate-orders/?search=花朵')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['count'], 1)
self.assertIn('花朵', response.data['results'][0]['style_name'])
else:
self.assertEqual(len(response.data), 1)
self.assertIn('花朵', response.data[0]['style_name'])
def test_ordering_by_created_at(self):
"""测试按创建时间排序"""
order1 = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='款式1',
)
order2 = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN002',
plate_type='平网',
style_name='款式2',
)
# 默认按创建时间倒序
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['results'][0]['id'], order2.id)
self.assertEqual(response.data['results'][1]['id'], order1.id)
else:
self.assertEqual(response.data[0]['id'], order2.id)
self.assertEqual(response.data[1]['id'], order1.id)
# 正序排列
response = self.client.get('/api/v1/plate-orders/?ordering=created_at')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
self.assertEqual(response.data['results'][0]['id'], order1.id)
self.assertEqual(response.data['results'][1]['id'], order2.id)
else:
self.assertEqual(response.data[0]['id'], order1.id)
self.assertEqual(response.data[1]['id'], order2.id)
def test_create_plate_order_with_process(self):
"""测试创建开版订单时指定流程"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN100',
'plate_type': '圆网',
'style_name': '测试款式',
'fabric': '棉布',
'process': self.process.id,
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['process'], self.process.id)
# 验证数据库中创建了 BusinessObject
plate_order = printing_models.PlateOrder.objects.get(id=response.data['id'])
self.assertIsNotNone(plate_order.business_object)
self.assertEqual(plate_order.business_object.process.id, self.process.id)
def test_create_plate_order_without_process(self):
"""测试创建开版订单时不指定流程(使用默认流程)"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN101',
'plate_type': '圆网',
'style_name': '测试款式',
'fabric': '棉布',
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# 应该使用默认流程
self.assertIsNotNone(response.data['process'])
# 验证使用了默认流程
plate_order = printing_models.PlateOrder.objects.get(id=response.data['id'])
from django.conf import settings
self.assertEqual(plate_order.process, settings.PLATE_ORDER_DEFAULT_PROCESS_ID)
def test_create_plate_order_with_invalid_process(self):
"""测试创建开版订单时指定无效的流程ID"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN102',
'plate_type': '圆网',
'style_name': '测试款式',
'fabric': '棉布',
'process': 99999, # 不存在的流程ID
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('process', response.data)
def test_retrieve_plate_order_with_process_name(self):
"""测试获取开版订单详情时包含流程名称"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN103',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
process=self.process.id,
)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['process'], self.process.id)
self.assertEqual(response.data['process_name'], self.process.name)
# 验证自动创建了 business_object
self.assertIsNotNone(response.data['business_object_id'])
def test_list_plate_orders_includes_process_name(self):
"""测试列表接口包含流程名称"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN104',
plate_type='圆网',
style_name='款式1',
process=self.process.id,
)
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
if isinstance(response.data, dict):
result = response.data['results'][0]
else:
result = response.data[0]
self.assertEqual(result['process'], self.process.id)
self.assertEqual(result['process_name'], self.process.name)
def test_update_plate_order_process(self):
"""测试更新开版订单的流程"""
# 创建另一个流程
process2 = stateflow_models.Process.objects.create(
name='快速开版流程',
description='简化的开版流程'
)
stateflow_models.ProcessNode.objects.create(process=process2, state=self.state1, order=0)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN105',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 获取原来的 business_object
old_business_object_id = plate_order.business_object.id if plate_order.business_object else None
# 更新流程(注意:这只会更新 process 字段,不会重新创建 business_object
data = {
'customer': self.customer.id,
'design_code': 'DESIGN105',
'plate_type': '圆网',
'style_name': '测试款式',
'process': process2.id,
}
response = self.client.put(f'/api/v1/plate-orders/{plate_order.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['process'], process2.id)
# 验证 business_object 没有改变(因为已经存在)
plate_order.refresh_from_db()
if old_business_object_id:
self.assertEqual(plate_order.business_object.id, old_business_object_id)
def test_business_object_auto_creation(self):
"""测试 BusinessObject 自动创建逻辑"""
# 直接创建 PlateOrder不通过 API
plate_order = printing_models.PlateOrder(
customer=self.customer,
design_code='DESIGN106',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 保存前没有 business_object
self.assertIsNone(plate_order.business_object)
# 保存后自动创建
plate_order.save()
plate_order.refresh_from_db()
self.assertIsNotNone(plate_order.business_object)
self.assertEqual(plate_order.business_object.process.id, self.process.id)
# 新逻辑BusinessObject 绑定回 PlateOrder
self.assertEqual(
plate_order.business_object.content_type_id,
ContentType.objects.get_for_model(printing_models.PlateOrder).id
)
self.assertEqual(plate_order.business_object.object_id, plate_order.id)
def test_advance_to_next_state(self):
"""测试推进到下一个状态"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN107',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 确保有 business_object
self.assertIsNotNone(plate_order.business_object)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/advance-to-next-state/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('detail', response.data)
self.assertIn('data', response.data)
# 验证状态改变
plate_order.refresh_from_db()
self.assertIsNotNone(plate_order.status)
def test_advance_to_next_state_without_business_object(self):
"""测试没有 business_object 时推进状态"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN108',
plate_type='圆网',
style_name='测试款式',
)
# 手动清除 business_object
plate_order.business_object = None
plate_order.save(update_fields=['business_object'])
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/advance-to-next-state/')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('没有关联的流程实例', response.data['detail'])
def test_step_back_one_state(self):
"""测试回退一步"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN109',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 先推进一步
from stateflow import services as stateflow_services
stateflow_services.advance_to_next_state(plate_order.business_object, self.user)
# 再回退
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/step-back-one-state/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('detail', response.data)
self.assertIn('data', response.data)
def test_completed_states(self):
"""测试查询已完成的流程列表"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN110',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 推进几步
from stateflow import services as stateflow_services
stateflow_services.advance_to_next_state(plate_order.business_object, self.user)
stateflow_services.advance_to_next_state(plate_order.business_object, self.user)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/completed-states/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('count', response.data)
self.assertIn('results', response.data)
self.assertEqual(response.data['count'], 2)
def test_completed_states_include_cancelled(self):
"""测试查询包含已撤销的流程"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN111',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/completed-states/?include_cancelled=true')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('count', response.data)
self.assertIn('results', response.data)
def test_timeline(self):
"""测试获取流程时间线"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN112',
plate_type='圆网',
style_name='测试款式',
process=self.process.id,
)
# 推进一步
from stateflow import services as stateflow_services
stateflow_services.advance_to_next_state(plate_order.business_object, self.user)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/timeline/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('count', response.data)
self.assertIn('results', response.data)
# 验证返回的数据结构
if response.data['count'] > 0:
first_item = response.data['results'][0]
self.assertIn('state_id', first_item)
self.assertIn('state_name', first_item)
self.assertIn('state_description', first_item)
self.assertIn('order', first_item)
self.assertIn('status', first_item)
def test_timeline_without_business_object(self):
"""测试没有 business_object 时获取时间线"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN113',
plate_type='圆网',
style_name='测试款式',
)
# 手动清除 business_object
plate_order.business_object = None
plate_order.save(update_fields=['business_object'])
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/timeline/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 0)
class PlateOrderInvalidateAPITestCase(TestCase):
"""测试 PlateOrder 作废/恢复 API"""
def setUp(self):
self.client = APIClient()
# 创建商户
self.merchant = basic_models.Merchant.objects.create(
name='测试商户',
type=basic_models.MerchantTypeEnum.FACTORY
)
# 创建用户
self.user = User.objects.create_user(
username='testuser',
password='testpass123',
email='test@example.com'
)
# 创建员工
self.employee = basic_models.Employee.objects.create(
sys_user=self.user,
merchant=self.merchant,
name='测试员工',
mobile='13800138000',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建业务员
self.salesperson = basic_models.Employee.objects.create(
merchant=self.merchant,
name='测试业务员',
mobile='13800138001',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建设计师
self.designer = basic_models.Employee.objects.create(
merchant=self.merchant,
name='测试设计师',
mobile='13800138003',
status=basic_models.EmployeeStatusEnum.ACTIVE
)
# 创建客户
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='测试客户',
mobile='13900139000',
area='测试地区'
)
# 认证用户
self.client.force_authenticate(user=self.user)
# 给用户添加基础权限 (需要 add/change 权限才能执行 POST action)
view_perm = Permission.objects.get(codename='view_plateorder')
add_perm = Permission.objects.get(codename='add_plateorder')
change_perm = Permission.objects.get(codename='change_plateorder')
self.user.user_permissions.add(view_perm, add_perm, change_perm)
# 创建测试流程(用于测试)
self.state1 = stateflow_models.State.objects.create(name='画图', description='画图阶段')
self.process = stateflow_models.Process.objects.create(
name='开版流程',
description='标准开版流程'
)
stateflow_models.ProcessNode.objects.create(process=self.process, state=self.state1, order=0)
def test_invalidate_plate_order_without_permission(self):
"""测试无权限作废开版订单"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/invalidate/')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
# 验证订单未被作废
plate_order.refresh_from_db()
self.assertFalse(plate_order.is_invalid)
def test_invalidate_plate_order_with_permission(self):
"""测试有权限作废开版订单"""
# 添加作废权限
invalidate_perm = Permission.objects.get(codename='can_invalidate_plateorder')
self.user.user_permissions.add(invalidate_perm)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/invalidate/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('开版订单已作废', response.data['detail'])
# 验证订单已被作废
plate_order.refresh_from_db()
self.assertTrue(plate_order.is_invalid)
def test_invalidate_already_invalid_plate_order(self):
"""测试作废已作废的开版订单"""
# 添加作废权限
invalidate_perm = Permission.objects.get(codename='can_invalidate_plateorder')
self.user.user_permissions.add(invalidate_perm)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
is_invalid=True,
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/invalidate/')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('已经作废', response.data['detail'])
def test_activate_plate_order_without_permission(self):
"""测试无权限恢复开版订单"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
is_invalid=True,
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/activate/')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
# 验证订单未被恢复
plate_order.refresh_from_db()
self.assertTrue(plate_order.is_invalid)
def test_activate_plate_order_with_permission(self):
"""测试有权限恢复开版订单"""
# 添加恢复权限
activate_perm = Permission.objects.get(codename='can_activate_plateorder')
self.user.user_permissions.add(activate_perm)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
is_invalid=True,
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/activate/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('开版订单已恢复', response.data['detail'])
# 验证订单已被恢复
plate_order.refresh_from_db()
self.assertFalse(plate_order.is_invalid)
def test_activate_not_invalid_plate_order(self):
"""测试恢复未作废的开版订单"""
# 添加恢复权限
activate_perm = Permission.objects.get(codename='can_activate_plateorder')
self.user.user_permissions.add(activate_perm)
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN001',
plate_type='圆网',
style_name='测试款式',
is_invalid=False,
)
response = self.client.post(f'/api/v1/plate-orders/{plate_order.id}/activate/')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('未作废', response.data['detail'])
def test_create_plate_order_with_designer(self):
"""测试创建带设计师的开版订单"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN_DESIGNER_001',
'plate_type': '圆网',
'style_name': '测试款式-设计师',
'fabric': '棉布',
'width': '150cm',
'designer': self.designer.id,
'salesperson': self.salesperson.id,
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data['designer'], self.designer.id)
# 验证数据库中创建了记录并关联了设计师
plate_order = printing_models.PlateOrder.objects.get(id=response.data['id'])
self.assertEqual(plate_order.designer, self.designer)
def test_create_plate_order_without_designer(self):
"""测试创建不带设计师的开版订单"""
data = {
'customer': self.customer.id,
'design_code': 'DESIGN_NO_DESIGNER',
'plate_type': '圆网',
'style_name': '测试款式-无设计师',
'fabric': '棉布',
'width': '150cm',
}
response = self.client.post('/api/v1/plate-orders/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIsNone(response.data['designer'])
def test_update_plate_order_designer(self):
"""测试更新开版订单的设计师"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN_UPDATE_DESIGNER',
plate_type='圆网',
style_name='测试款式',
fabric='棉布',
)
data = {
'designer': self.designer.id,
}
response = self.client.patch(f'/api/v1/plate-orders/{plate_order.id}/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['designer'], self.designer.id)
# 验证数据库已更新
plate_order.refresh_from_db()
self.assertEqual(plate_order.designer, self.designer)
def test_list_plate_orders_includes_designer_name(self):
"""测试列表接口包含设计师字段"""
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN_LIST_1',
plate_type='圆网',
style_name='测试款式1',
designer=self.designer,
)
printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN_LIST_2',
plate_type='平网',
style_name='测试款式2',
)
response = self.client.get('/api/v1/plate-orders/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data['results'] if isinstance(response.data, dict) else response.data
# 验证数据包含designer字段designer_name在designer为空时可能不存在
self.assertIn('designer', results[0])
def test_retrieve_plate_order_includes_designer(self):
"""测试详情接口包含设计师信息"""
plate_order = printing_models.PlateOrder.objects.create(
customer=self.customer,
design_code='DESIGN_RETRIEVE',
plate_type='圆网',
style_name='测试款式',
designer=self.designer,
salesperson=self.salesperson,
)
response = self.client.get(f'/api/v1/plate-orders/{plate_order.id}/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['designer'], self.designer.id)
self.assertIn('designer_name', response.data)