""" PlateOrder API 测试 """ from django.core.files.uploadedfile import SimpleUploadedFile 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.assertTrue(printing_models.PlateOrder.objects.filter(design_code='DESIGN001').exists()) 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) 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_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) 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.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_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) 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)