""" PrintingJob API 测试 """ from django.test import TestCase from django.conf import settings 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 django.contrib.contenttypes.models import ContentType from basic_info import models as basic_models from printing import models as printing_models from stateflow import models as stateflow_models User = get_user_model() class PrintingJobAPITestCase(TestCase): """测试 PrintingJob 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.customer = basic_models.Customer.objects.create( merchant=self.merchant, name='测试客户', mobile='13900139000', area='测试地区' ) # 创建流程 self.state1 = stateflow_models.State.objects.create(name='待印染') self.state2 = stateflow_models.State.objects.create(name='印染中') self.state3 = stateflow_models.State.objects.create(name='已完成') self.process = stateflow_models.Process.objects.create(name='印染流程') self.process.replace_nodes([self.state1, self.state2, self.state3]) # 设置默认流程 settings.PRINTING_DEFAULT_PROCESS_ID = self.process.id # 创建印染订单 self.printing_order = printing_models.PrintingOrder.objects.create( customer=self.customer, fabric='测试布料', width='150cm', process=self.process, ) # 创建产品类别 self.category = basic_models.ProductCategory.objects.create( merchant=self.merchant, name='测试类别' ) # 创建产品 self.product = basic_models.Product.objects.create( merchant=self.merchant, category=self.category, name='测试产品', human_id='TEST001', unit=basic_models.ProductUnitEnum.METER ) # 认证用户 self.client.force_authenticate(user=self.user) # 给用户添加基础权限 view_perm = Permission.objects.get(codename='view_printingjob') add_perm = Permission.objects.get(codename='add_printingjob') change_perm = Permission.objects.get(codename='change_printingjob') self.user.user_permissions.add(view_perm, add_perm, change_perm) def test_create_printing_job(self): """测试创建印染款式明细""" data = { 'printing_order': self.printing_order.id, 'product': self.product.id, 'quantity': 100, 'unit': '米', 'size': '50*60', 'pieces': 10, 'description': '测试备注' } response = self.client.post('/api/v1/printing-jobs/', data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) # 新增字段:批量推进记录(稳定输出 key,默认空数组) self.assertIn('batch_advance_records', response.data) self.assertIsInstance(response.data['batch_advance_records'], list) self.assertEqual(len(response.data['batch_advance_records']), 0) # 验证创建成功 job = printing_models.PrintingJob.objects.filter( printing_order=self.printing_order, product=self.product ).first() self.assertIsNotNone(job) self.assertEqual(job.quantity, 100) self.assertEqual(job.unit, '米') self.assertEqual(job.pieces, 10) def test_list_printing_jobs(self): """测试获取款式明细列表""" # 创建测试数据 job1 = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) job2 = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=200, unit='米', size='60*70', pieces=20 ) response = self.client.get('/api/v1/printing-jobs/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 2) # 新增字段:批量推进记录(稳定输出 key) for item in response.data: self.assertIn('batch_advance_records', item) self.assertIsInstance(item['batch_advance_records'], list) self.assertEqual(len(item['batch_advance_records']), 0) def test_retrieve_printing_job(self): """测试获取款式明细详情""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10, description='详情测试' ) response = self.client.get(f'/api/v1/printing-jobs/{job.id}/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['quantity'], 100) self.assertEqual(response.data['unit'], '米') self.assertIn('product_name', response.data) self.assertEqual(response.data['product_name'], self.product.name) # 新增字段:批量推进记录(稳定输出 key) self.assertIn('batch_advance_records', response.data) self.assertIsInstance(response.data['batch_advance_records'], list) self.assertEqual(len(response.data['batch_advance_records']), 0) def test_batch_advance_records_in_list_and_detail(self): """测试 PrintingJob 序列化输出包含批量推进记录(有记录时返回明细,无记录返回空)""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10, description='批量记录测试' ) # 创建一条批量推进记录并关联该 job record = printing_models.PrintingJobBatchAdvanceRecord.objects.create( printing_order=self.printing_order, state=self.state1, created_by=self.user, parameters={'temperature': '25.5', 'operator': '张三'}, ) record.printing_jobs.add(job) # list: 应包含 batch_advance_records response = self.client.get('/api/v1/printing-jobs/') self.assertEqual(response.status_code, status.HTTP_200_OK) target = next(item for item in response.data if item['id'] == job.id) self.assertIn('batch_advance_records', target) self.assertEqual(len(target['batch_advance_records']), 1) rec = target['batch_advance_records'][0] self.assertEqual(rec['id'], record.id) self.assertEqual(rec['printing_order'], self.printing_order.id) self.assertEqual(rec['state'], self.state1.id) self.assertEqual(rec['state_id'], self.state1.id) self.assertEqual(rec['state_name'], self.state1.name) self.assertEqual(rec['created_by'], self.user.id) self.assertEqual(rec['created_by_username'], self.user.username) self.assertEqual(rec['created_by_name'], self.employee.name) self.assertEqual(rec['parameters']['temperature'], '25.5') self.assertEqual(rec['parameters']['operator'], '张三') # detail: 同样应包含 batch_advance_records response = self.client.get(f'/api/v1/printing-jobs/{job.id}/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('batch_advance_records', response.data) self.assertEqual(len(response.data['batch_advance_records']), 1) def test_update_printing_job(self): """测试更新款式明细""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) update_data = { 'printing_order': self.printing_order.id, 'product': self.product.id, 'quantity': 200, 'unit': '码', 'size': '60*70', 'pieces': 20, 'description': '更新后的备注' } response = self.client.put( f'/api/v1/printing-jobs/{job.id}/', update_data, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) job.refresh_from_db() self.assertEqual(job.quantity, 200) self.assertEqual(job.unit, '码') self.assertEqual(job.pieces, 20) def test_partial_update_printing_job(self): """测试部分更新款式明细""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) patch_data = { 'quantity': 150, 'pieces': 15 } response = self.client.patch( f'/api/v1/printing-jobs/{job.id}/', patch_data, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) job.refresh_from_db() self.assertEqual(job.quantity, 150) self.assertEqual(job.pieces, 15) self.assertEqual(job.unit, '米') # 未修改字段保持不变 def test_delete_printing_job_forbidden(self): """测试删除款式明细被禁用""" # 添加删除权限以便测试destroy方法的自定义逻辑 delete_perm = Permission.objects.get(codename='delete_printingjob') self.user.user_permissions.add(delete_perm) job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) response = self.client.delete(f'/api/v1/printing-jobs/{job.id}/') self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) self.assertIn('不支持删除', response.data['detail']) # 验证明细仍然存在 self.assertTrue( printing_models.PrintingJob.objects.filter(id=job.id).exists() ) def test_filter_by_printing_order(self): """测试按印染订单过滤""" order2 = printing_models.PrintingOrder.objects.create( customer=self.customer, fabric='其他布料', width='160cm' ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) printing_models.PrintingJob.objects.create( printing_order=order2, product=self.product, quantity=200, unit='米', size='60*70', pieces=20 ) response = self.client.get(f'/api/v1/printing-jobs/?printing_order={self.printing_order.id}') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) self.assertEqual(response.data[0]['printing_order'], self.printing_order.id) def test_filter_by_product(self): """测试按产品过滤""" product2 = basic_models.Product.objects.create( merchant=self.merchant, category=self.category, name='产品2', human_id='TEST002', unit=basic_models.ProductUnitEnum.METER ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=product2, quantity=200, unit='米', size='60*70', pieces=20 ) response = self.client.get(f'/api/v1/printing-jobs/?product={self.product.id}') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) self.assertEqual(response.data[0]['product'], self.product.id) def test_filter_by_quantity_range(self): """测试按数量范围过滤""" printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=50, unit='米', size='50*60', pieces=10 ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=150, unit='米', size='60*70', pieces=20 ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=250, unit='米', size='70*80', pieces=30 ) response = self.client.get('/api/v1/printing-jobs/?quantity_min=100&quantity_max=200') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) self.assertEqual(response.data[0]['quantity'], 150) def test_search_by_product_name(self): """测试按产品名称搜索""" product2 = basic_models.Product.objects.create( merchant=self.merchant, category=self.category, name='特殊产品', human_id='TEST003', unit=basic_models.ProductUnitEnum.METER ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=product2, quantity=200, unit='米', size='60*70', pieces=20 ) response = self.client.get('/api/v1/printing-jobs/?search=特殊') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) self.assertIn('特殊', response.data[0]['product_name']) def test_ordering(self): """测试排序""" job1 = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) job2 = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=200, unit='米', size='60*70', pieces=20 ) # 按数量升序 response = self.client.get('/api/v1/printing-jobs/?ordering=quantity') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data[0]['quantity'], 100) self.assertEqual(response.data[1]['quantity'], 200) # 按数量降序 response = self.client.get('/api/v1/printing-jobs/?ordering=-quantity') self.assertEqual(response.data[0]['quantity'], 200) self.assertEqual(response.data[1]['quantity'], 100) def test_validate_quantity_positive(self): """测试数量必须大于0""" data = { 'printing_order': self.printing_order.id, 'product': self.product.id, 'quantity': 0, # 无效数量 'unit': '米', 'size': '50*60', 'pieces': 10 } response = self.client.post('/api/v1/printing-jobs/', data, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('quantity', response.data) def test_validate_pieces_can_be_zero(self): """测试件数可以为0或null""" data = { 'printing_order': self.printing_order.id, 'product': self.product.id, 'quantity': 100, 'unit': '米', 'size': '50*60', 'pieces': 0 # 允许为0 } response = self.client.post('/api/v1/printing-jobs/', data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) # 验证创建成功 job = printing_models.PrintingJob.objects.get(id=response.data['id']) self.assertEqual(job.pieces, 0) def test_create_job_with_business_object(self): """测试创建任务自动创建 BusinessObject""" data = { 'printing_order': self.printing_order.id, 'product': self.product.id, 'quantity': 100, 'unit': '米', 'size': '50*60', 'pieces': 10 } response = self.client.post('/api/v1/printing-jobs/', data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) # 验证 BusinessObject 已创建 job = printing_models.PrintingJob.objects.get(id=response.data['id']) self.assertIsNotNone(job.business_object) self.assertEqual(job.business_object.process, self.process) # 新逻辑:BusinessObject 绑定回 PrintingJob self.assertEqual(job.business_object.content_type_id, ContentType.objects.get_for_model(printing_models.PrintingJob).id) self.assertEqual(job.business_object.object_id, job.id) def test_job_status_in_detail(self): """测试任务详情包含状态字段""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() response = self.client.get(f'/api/v1/printing-jobs/{job.id}/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证状态字段 self.assertIn('status', response.data) self.assertIn('status_id', response.data) self.assertIn('is_completed', response.data) self.assertIn('has_started', response.data) self.assertIn('business_object_id', response.data) # 未推进状态,但有 BusinessObject,当前是初始状态 self.assertIn(response.data['status'], ['待印染', '未开始']) # 初始状态或未开始 self.assertFalse(response.data['is_completed']) self.assertFalse(response.data['has_started']) def test_job_status_in_list(self): """测试任务列表包含状态字段""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) response = self.client.get('/api/v1/printing-jobs/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证列表中的状态字段 self.assertIn('status', response.data[0]) self.assertIn('is_completed', response.data[0]) def test_job_completion_status(self): """测试任务完成状态判断""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进到最后一个状态(流程有3个节点,需要推进3次) from stateflow.services import advance_to_next_state advance_to_next_state(business_object, self.user) # 完成第1个状态 advance_to_next_state(business_object, self.user) # 完成第2个状态 advance_to_next_state(business_object, self.user) # 完成第3个状态 response = self.client.get(f'/api/v1/printing-jobs/{job.id}/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证完成状态 self.assertEqual(response.data['status'], '已完成') self.assertTrue(response.data['is_completed']) self.assertTrue(response.data['has_started']) def test_advance_to_next_state(self): """测试推进到下一个状态 API""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进到下一个状态 response = self.client.post(f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('detail', response.data) self.assertIn('data', response.data) self.assertIn('已完成状态', response.data['detail']) # 验证状态已更新 job.refresh_from_db() self.assertTrue(job.has_started) self.assertEqual(job.status, self.state2.name) # 应该是第二个状态(当前进行中的) def test_advance_to_next_state_without_business_object(self): """测试推进到下一个状态 - 没有 BusinessObject 的情况""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) response = self.client.post(f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('没有关联的流程实例', response.data['detail']) def test_advance_with_parameters(self): """测试推进到下一个状态 - 带参数""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进到下一个状态,带参数 data = { 'parameters': { 'temperature': '25.5', 'operator': '张三', 'humidity': '60%' } } response = self.client.post( f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/', data, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('detail', response.data) self.assertIn('data', response.data) # 验证参数已保存 job.refresh_from_db() state_log = business_object.state_logs.filter(state=self.state1, is_cancelled=False).first() self.assertIsNotNone(state_log) # 验证参数记录 param_record = state_log.parameter_records.first() self.assertIsNotNone(param_record) self.assertEqual(param_record.parameters['temperature'], '25.5') self.assertEqual(param_record.parameters['operator'], '张三') self.assertEqual(param_record.parameters['humidity'], '60%') def test_advance_with_required_parameters_missing(self): """测试推进到下一个状态 - 缺少必填参数""" # 创建带必填参数的状态 param1 = stateflow_models.StateParameter.objects.create( key='temperature', is_required=True, description='温度' ) param2 = stateflow_models.StateParameter.objects.create( key='operator', is_required=True, description='操作员' ) state_with_params = stateflow_models.State.objects.create(name='带参数状态') state_with_params.parameters.add(param1, param2) # 创建新流程,第一个状态是带必填参数的 process_with_params = stateflow_models.Process.objects.create(name='带参数流程') process_with_params.replace_nodes([state_with_params, self.state2]) job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=process_with_params, ) job.business_object = business_object job.save() # 尝试推进但不提供必填参数 response = self.client.post(f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('缺失必填参数', response.data['detail']) self.assertIn('temperature', response.data['detail']) self.assertIn('operator', response.data['detail']) def test_advance_with_partial_required_parameters(self): """测试推进到下一个状态 - 只提供部分必填参数""" # 创建带必填参数的状态 param1 = stateflow_models.StateParameter.objects.create( key='temperature', is_required=True, description='温度' ) param2 = stateflow_models.StateParameter.objects.create( key='operator', is_required=True, description='操作员' ) state_with_params = stateflow_models.State.objects.create(name='带参数状态') state_with_params.parameters.add(param1, param2) # 创建新流程 process_with_params = stateflow_models.Process.objects.create(name='带参数流程') process_with_params.replace_nodes([state_with_params, self.state2]) job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=process_with_params, ) job.business_object = business_object job.save() # 只提供一个必填参数 data = { 'parameters': { 'temperature': '25.5' } } response = self.client.post( f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/', data, format='json' ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('缺失必填参数', response.data['detail']) self.assertIn('operator', response.data['detail']) self.assertNotIn('temperature', response.data['detail']) # temperature 已提供 def test_advance_through_all_states(self): """测试推进到完成所有状态""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进 3 次完成所有状态 for i in range(3): response = self.client.post(f'/api/v1/printing-jobs/{job.id}/advance-to-next-state/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证已完成 job.refresh_from_db() self.assertTrue(job.is_completed) self.assertEqual(job.status, '已完成') def test_step_back_one_state(self): """测试回退一个状态 API""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 先推进两步 from stateflow.services import advance_to_next_state advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) # 回退一步 response = self.client.post(f'/api/v1/printing-jobs/{job.id}/step-back-one-state/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('detail', response.data) self.assertIn('data', response.data) self.assertIn('已回退状态', response.data['detail']) # 验证状态已回退 job.refresh_from_db() self.assertEqual(job.status, self.state2.name) # 应该回退到第二个状态(当前) def test_step_back_without_business_object(self): """测试回退状态 - 没有 BusinessObject 的情况""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) response = self.client.post(f'/api/v1/printing-jobs/{job.id}/step-back-one-state/') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('没有关联的流程实例', response.data['detail']) def test_step_back_without_records(self): """测试回退状态 - 没有状态流转记录的情况""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject 但不推进状态 business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() response = self.client.post(f'/api/v1/printing-jobs/{job.id}/step-back-one-state/') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn('没有任何状态流转记录', response.data['detail']) def test_completed_states_list(self): """测试获取已完成状态列表 API""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进两个状态 from stateflow.services import advance_to_next_state advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) response = self.client.get(f'/api/v1/printing-jobs/{job.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) self.assertEqual(len(response.data['results']), 2) # 验证返回的状态数据 first_state = response.data['results'][0] self.assertIn('state_id', first_state) self.assertIn('state_name', first_state) self.assertIn('completed_at', first_state) self.assertIn('completed_by', first_state) self.assertIn('is_cancelled', first_state) self.assertFalse(first_state['is_cancelled']) def test_completed_states_include_cancelled(self): """测试获取已完成状态列表 - 包含已撤销的记录""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进两个状态 from stateflow.services import advance_to_next_state, step_back_one_state advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) # 回退一次(会将最后一条记录标记为 cancelled) step_back_one_state(business_object, self.user) # 不包含已撤销的记录(默认) response = self.client.get(f'/api/v1/printing-jobs/{job.id}/completed-states/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 1) # 包含已撤销的记录 response = self.client.get(f'/api/v1/printing-jobs/{job.id}/completed-states/?include_cancelled=true') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 2) # 验证有一条记录是已撤销的 cancelled_records = [r for r in response.data['results'] if r['is_cancelled']] self.assertEqual(len(cancelled_records), 1) def test_completed_states_empty(self): """测试获取已完成状态列表 - 没有完成任何状态""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject 但不推进状态 business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() response = self.client.get(f'/api/v1/printing-jobs/{job.id}/completed-states/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 0) self.assertEqual(len(response.data['results']), 0) def test_timeline(self): """测试获取流程时间线 API""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进一个状态 from stateflow.services import advance_to_next_state advance_to_next_state(business_object, self.user) response = self.client.get(f'/api/v1/printing-jobs/{job.id}/timeline/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn('count', response.data) self.assertIn('results', response.data) self.assertEqual(response.data['count'], 3) # 流程有 3 个状态 self.assertEqual(len(response.data['results']), 3) # 验证时间线数据 timeline = response.data['results'] # 第一个状态应该是已完成 self.assertEqual(timeline[0]['state_name'], self.state1.name) self.assertEqual(timeline[0]['status'], 'completed') self.assertIsNotNone(timeline[0]['completed_at']) self.assertIsNotNone(timeline[0]['completed_by']) # 第二个状态应该是未开始(因为我们废除了 in_progress 的概念) self.assertEqual(timeline[1]['state_name'], self.state2.name) self.assertEqual(timeline[1]['status'], 'not_started') self.assertIsNone(timeline[1]['completed_at']) # 第三个状态应该是未开始 self.assertEqual(timeline[2]['state_name'], self.state3.name) self.assertEqual(timeline[2]['status'], 'not_started') self.assertIsNone(timeline[2]['completed_at']) def test_timeline_all_completed(self): """测试获取流程时间线 - 所有状态已完成""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 完成所有状态 from stateflow.services import advance_to_next_state advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) response = self.client.get(f'/api/v1/printing-jobs/{job.id}/timeline/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证所有状态都是已完成 timeline = response.data['results'] for item in timeline: self.assertEqual(item['status'], 'completed') self.assertIsNotNone(item['completed_at']) def test_timeline_not_started(self): """测试获取流程时间线 - 未开始任何状态""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject 但不推进状态 business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() response = self.client.get(f'/api/v1/printing-jobs/{job.id}/timeline/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证第一个状态是未开始,因为没有推进任何状态 timeline = response.data['results'] self.assertEqual(timeline[0]['status'], 'not_started') self.assertEqual(timeline[1]['status'], 'not_started') self.assertEqual(timeline[2]['status'], 'not_started') def test_timeline_excludes_cancelled(self): """测试获取流程时间线 - 自动过滤已撤销的记录""" job = printing_models.PrintingJob.objects.create( printing_order=self.printing_order, product=self.product, quantity=100, unit='米', size='50*60', pieces=10 ) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( name=f'PrintingJob-{job.id}', process=self.process, ) job.business_object = business_object job.save() # 推进两个状态然后回退一次 from stateflow.services import advance_to_next_state, step_back_one_state advance_to_next_state(business_object, self.user) advance_to_next_state(business_object, self.user) step_back_one_state(business_object, self.user) response = self.client.get(f'/api/v1/printing-jobs/{job.id}/timeline/') self.assertEqual(response.status_code, status.HTTP_200_OK) # 验证时间线中只有一个已完成的状态(撤销的不显示) timeline = response.data['results'] completed_states = [t for t in timeline if t['status'] == 'completed'] self.assertEqual(len(completed_states), 1) self.assertEqual(completed_states[0]['state_name'], self.state1.name)