forked from erp-dev/erp
patch: patch testing (api & services)
This commit is contained in:
@@ -257,12 +257,15 @@ class PrintingJobViewSet(viewsets.ModelViewSet):
|
|||||||
create: 创建款式明细
|
create: 创建款式明细
|
||||||
update: 更新款式明细
|
update: 更新款式明细
|
||||||
partial_update: 部分更新款式明细
|
partial_update: 部分更新款式明细
|
||||||
|
advance_to_next_state: 推进到下一个状态
|
||||||
|
step_back_one_state: 回退一步
|
||||||
completed_states: 查询已完成的流程列表
|
completed_states: 查询已完成的流程列表
|
||||||
timeline: 获取流程时间线
|
timeline: 获取流程时间线
|
||||||
|
|
||||||
注意:状态流转功能已迁移至 stateflow 模块的业务对象接口
|
注意:状态流转接口是对 stateflow 模块的便捷包装
|
||||||
- 推进到下一步: POST /api/v1/stateflow/business-objects/{business_object_id}/advance/
|
- 前端只需提供 printing_job 的 ID
|
||||||
- 回退一步: POST /api/v1/stateflow/business-objects/{business_object_id}/step_back/
|
- 系统会自动查找关联的 business_object 并进行状态流转
|
||||||
|
- 也可以直接使用 stateflow 的原始接口(需要 business_object_id)
|
||||||
|
|
||||||
查询参数:
|
查询参数:
|
||||||
- printing_order: 印染订单ID
|
- printing_order: 印染订单ID
|
||||||
@@ -308,6 +311,86 @@ class PrintingJobViewSet(viewsets.ModelViewSet):
|
|||||||
status=status.HTTP_405_METHOD_NOT_ALLOWED
|
status=status.HTTP_405_METHOD_NOT_ALLOWED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
|
||||||
|
def advance_to_next_state(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
推进到下一个状态
|
||||||
|
|
||||||
|
将印染任务推进到下一个流程状态
|
||||||
|
|
||||||
|
注意:这是对 stateflow.BusinessObject.advance 接口的便捷包装,
|
||||||
|
自动通过 printing_job 查找关联的 business_object 并调用状态流转服务。
|
||||||
|
前端只需提供 printing_job 的 ID,无需关心 business_object_id。
|
||||||
|
"""
|
||||||
|
job = self.get_object()
|
||||||
|
|
||||||
|
if not job.business_object:
|
||||||
|
return Response(
|
||||||
|
{'detail': '该任务没有关联的流程实例'},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 调用 stateflow 统一服务进行状态流转
|
||||||
|
from stateflow import services as stateflow_services
|
||||||
|
success, message, state_log = stateflow_services.advance_to_next_state(
|
||||||
|
job.business_object, request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return Response(
|
||||||
|
{'detail': message},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 重新获取 job 以刷新状态
|
||||||
|
job.refresh_from_db()
|
||||||
|
serializer = PrintingJobDetailSerializer(job)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'detail': message,
|
||||||
|
'data': serializer.data
|
||||||
|
})
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post'], url_path='step-back-one-state')
|
||||||
|
def step_back_one_state(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
回退一步
|
||||||
|
|
||||||
|
将印染任务回退到上一个流程状态
|
||||||
|
|
||||||
|
注意:这是对 stateflow.BusinessObject.step_back 接口的便捷包装,
|
||||||
|
自动通过 printing_job 查找关联的 business_object 并调用状态流转服务。
|
||||||
|
前端只需提供 printing_job 的 ID,无需关心 business_object_id。
|
||||||
|
"""
|
||||||
|
job = self.get_object()
|
||||||
|
|
||||||
|
if not job.business_object:
|
||||||
|
return Response(
|
||||||
|
{'detail': '该任务没有关联的流程实例'},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 调用 stateflow 统一服务进行状态回退
|
||||||
|
from stateflow import services as stateflow_services
|
||||||
|
success, message = stateflow_services.step_back_one_state(
|
||||||
|
job.business_object, request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return Response(
|
||||||
|
{'detail': message},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 重新获取 job 以刷新状态
|
||||||
|
job.refresh_from_db()
|
||||||
|
serializer = PrintingJobDetailSerializer(job)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'detail': message,
|
||||||
|
'data': serializer.data
|
||||||
|
})
|
||||||
|
|
||||||
@action(detail=True, methods=['get'], url_path='completed-states')
|
@action(detail=True, methods=['get'], url_path='completed-states')
|
||||||
def completed_states(self, request, pk=None):
|
def completed_states(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
@@ -452,12 +535,15 @@ class PlateOrderViewSet(viewsets.ModelViewSet):
|
|||||||
partial_update: 部分更新开版订单
|
partial_update: 部分更新开版订单
|
||||||
invalidate: 作废开版订单
|
invalidate: 作废开版订单
|
||||||
activate: 恢复开版订单
|
activate: 恢复开版订单
|
||||||
|
advance_to_next_state: 推进到下一个状态(便捷包装)
|
||||||
|
step_back_one_state: 回退一步(便捷包装)
|
||||||
completed_states: 查询已完成的流程列表
|
completed_states: 查询已完成的流程列表
|
||||||
timeline: 获取流程时间线
|
timeline: 获取流程时间线
|
||||||
|
|
||||||
注意:状态流转功能已迁移至 stateflow 模块的业务对象接口
|
注意:状态流转接口是对 stateflow 模块的便捷包装
|
||||||
- 推进到下一步: POST /api/v1/stateflow/business-objects/{business_object_id}/advance/
|
- 前端只需提供 plate_order 的 ID
|
||||||
- 回退一步: POST /api/v1/stateflow/business-objects/{business_object_id}/step_back/
|
- 系统会自动查找关联的 business_object 并进行状态流转
|
||||||
|
- 也可以直接使用 stateflow 的原始接口(需要 business_object_id)
|
||||||
|
|
||||||
查询参数:
|
查询参数:
|
||||||
- customer: 客户ID
|
- customer: 客户ID
|
||||||
@@ -587,6 +673,86 @@ class PlateOrderViewSet(viewsets.ModelViewSet):
|
|||||||
'data': serializer.data
|
'data': serializer.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
|
||||||
|
def advance_to_next_state(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
推进到下一个状态
|
||||||
|
|
||||||
|
将开版订单推进到下一个流程状态
|
||||||
|
|
||||||
|
注意:这是对 stateflow.BusinessObject.advance 接口的便捷包装,
|
||||||
|
自动通过 plate_order 查找关联的 business_object 并调用状态流转服务。
|
||||||
|
"""
|
||||||
|
plate_order = self.get_object()
|
||||||
|
|
||||||
|
if not plate_order.business_object:
|
||||||
|
return Response(
|
||||||
|
{'detail': '该开版订单没有关联的流程实例'},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 调用 stateflow 统一服务进行状态流转
|
||||||
|
from stateflow import services as stateflow_services
|
||||||
|
success, message, state_log = stateflow_services.advance_to_next_state(
|
||||||
|
plate_order.business_object, request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return Response(
|
||||||
|
{'detail': message},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 重新获取 plate_order 以刷新状态
|
||||||
|
plate_order.refresh_from_db()
|
||||||
|
from .serializers import PlateOrderDetailSerializer
|
||||||
|
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'detail': message,
|
||||||
|
'data': serializer.data
|
||||||
|
})
|
||||||
|
|
||||||
|
@action(detail=True, methods=['post'], url_path='step-back-one-state')
|
||||||
|
def step_back_one_state(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
回退一步
|
||||||
|
|
||||||
|
将开版订单回退到上一个流程状态
|
||||||
|
|
||||||
|
注意:这是对 stateflow.BusinessObject.step_back 接口的便捷包装,
|
||||||
|
自动通过 plate_order 查找关联的 business_object 并调用状态流转服务。
|
||||||
|
"""
|
||||||
|
plate_order = self.get_object()
|
||||||
|
|
||||||
|
if not plate_order.business_object:
|
||||||
|
return Response(
|
||||||
|
{'detail': '该开版订单没有关联的流程实例'},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 调用 stateflow 统一服务进行状态回退
|
||||||
|
from stateflow import services as stateflow_services
|
||||||
|
success, message = stateflow_services.step_back_one_state(
|
||||||
|
plate_order.business_object, request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return Response(
|
||||||
|
{'detail': message},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 重新获取 plate_order 以刷新状态
|
||||||
|
plate_order.refresh_from_db()
|
||||||
|
from .serializers import PlateOrderDetailSerializer
|
||||||
|
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'detail': message,
|
||||||
|
'data': serializer.data
|
||||||
|
})
|
||||||
|
|
||||||
@action(detail=True, methods=['get'], url_path='completed-states')
|
@action(detail=True, methods=['get'], url_path='completed-states')
|
||||||
def completed_states(self, request, pk=None):
|
def completed_states(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -134,26 +134,33 @@ class PlateOrder(ModelBase):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self) -> str:
|
def status(self) -> str:
|
||||||
"""返回当前状态名称"""
|
"""
|
||||||
|
返回当前状态名称(下一个待执行的状态名称)
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- current_state 为 None:返回"已完成"(没有待执行的节点)
|
||||||
|
- current_state 不为 None + 进度 0%:返回"未开始"
|
||||||
|
- current_state 不为 None + 进度 > 0%:返回 current_state.name(正在进行)
|
||||||
|
"""
|
||||||
if not self.business_object:
|
if not self.business_object:
|
||||||
return '未开始'
|
return '未开始'
|
||||||
|
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
|
|
||||||
# current_state 为 None 有两种情况:未开始或已完成
|
|
||||||
if current_state is None:
|
if current_state is None:
|
||||||
# 检查是否有完成记录来区分
|
# 没有待执行的节点,已完成
|
||||||
has_completed_records = self.business_object.state_logs.filter(is_cancelled=False).exists()
|
|
||||||
if has_completed_records:
|
|
||||||
return '已完成'
|
return '已完成'
|
||||||
else:
|
|
||||||
|
# 有待执行的节点,配合进度判断是"未开始"还是"进行中"
|
||||||
|
progress = self.business_object.get_progress_percentage()
|
||||||
|
if progress == 0:
|
||||||
return '未开始'
|
return '未开始'
|
||||||
|
|
||||||
return current_state.name
|
return current_state.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_id(self):
|
def status_id(self):
|
||||||
"""返回当前状态ID"""
|
"""返回当前状态ID(下一个待执行的状态ID)"""
|
||||||
if not self.business_object:
|
if not self.business_object:
|
||||||
return None
|
return None
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
@@ -291,26 +298,33 @@ class PrintingJob(ModelBase):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self) -> str:
|
def status(self) -> str:
|
||||||
"""返回当前状态名称"""
|
"""
|
||||||
|
返回当前状态名称(下一个待执行的状态名称)
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- current_state 为 None:返回"已完成"(没有待执行的节点)
|
||||||
|
- current_state 不为 None + 进度 0%:返回"未开始"
|
||||||
|
- current_state 不为 None + 进度 > 0%:返回 current_state.name(正在进行)
|
||||||
|
"""
|
||||||
if not self.business_object:
|
if not self.business_object:
|
||||||
return '未开始'
|
return '未开始'
|
||||||
|
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
|
|
||||||
# current_state 为 None 有两种情况:未开始或已完成
|
|
||||||
if current_state is None:
|
if current_state is None:
|
||||||
# 检查是否有完成记录来区分
|
# 没有待执行的节点,已完成
|
||||||
has_completed_records = self.business_object.state_logs.filter(is_cancelled=False).exists()
|
|
||||||
if has_completed_records:
|
|
||||||
return '已完成'
|
return '已完成'
|
||||||
else:
|
|
||||||
|
# 有待执行的节点,配合进度判断是"未开始"还是"进行中"
|
||||||
|
progress = self.business_object.get_progress_percentage()
|
||||||
|
if progress == 0:
|
||||||
return '未开始'
|
return '未开始'
|
||||||
|
|
||||||
return current_state.name
|
return current_state.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_id(self):
|
def status_id(self):
|
||||||
"""返回当前状态ID"""
|
"""返回当前状态ID(下一个待执行的状态ID)"""
|
||||||
if not self.business_object:
|
if not self.business_object:
|
||||||
return None
|
return None
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class PlateOrderModelTestCase(TestCase):
|
|||||||
|
|
||||||
# 刷新并检查状态
|
# 刷新并检查状态
|
||||||
plate_order.refresh_from_db()
|
plate_order.refresh_from_db()
|
||||||
self.assertEqual(plate_order.status, state1.name) # current_state 是最后完成的状态(state1)
|
self.assertEqual(plate_order.status, state2.name) # current_state 是下一个待执行的状态(state2)
|
||||||
self.assertFalse(plate_order.is_completed)
|
self.assertFalse(plate_order.is_completed)
|
||||||
self.assertTrue(plate_order.has_started)
|
self.assertTrue(plate_order.has_started)
|
||||||
self.assertGreater(plate_order.progress_percentage, 0)
|
self.assertGreater(plate_order.progress_percentage, 0)
|
||||||
|
|||||||
@@ -11,27 +11,24 @@ User = get_user_model()
|
|||||||
|
|
||||||
def get_business_object_current_state(business_object: 'models.BusinessObject') -> Optional['models.State']:
|
def get_business_object_current_state(business_object: 'models.BusinessObject') -> Optional['models.State']:
|
||||||
"""
|
"""
|
||||||
获取业务对象的当前状态(最后完成的状态)
|
获取业务对象的当前状态(下一个待执行的节点)
|
||||||
|
|
||||||
规则:
|
规则:
|
||||||
1. 如果没有任何完成记录(或所有记录都被撤销),返回 None(未开始)
|
- 返回下一个待执行的节点(第一个未完成的节点)
|
||||||
2. 如果有完成记录,返回最后完成的状态(进行中或已完成)
|
- 如果所有节点都已完成,返回 None
|
||||||
|
|
||||||
注意:current_state 表示"最后完成的状态",而不是"待执行的状态"
|
注意:current_state 始终返回"下一个待执行的节点",配合进度可以判断具体状态
|
||||||
|
- 进度 0% + current_state 非空 → 显示"未开始"(还没开始执行,但知道第一个节点是什么)
|
||||||
|
- 进度 > 0% + current_state 非空 → 显示 current_state.name(正在进行)
|
||||||
|
- current_state = None → 显示"已完成"(没有待执行的节点了)
|
||||||
"""
|
"""
|
||||||
# 获取最后一条完成且未撤销的记录
|
# 使用 get_next_pending_state 获取下一个待执行的节点
|
||||||
last_completed_record = (
|
next_pending = get_next_pending_state(business_object, include_parameters=False)
|
||||||
business_object.state_logs
|
|
||||||
.filter(is_cancelled=False)
|
|
||||||
.select_related('state')
|
|
||||||
.order_by('-completed_at', '-id')
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
|
|
||||||
if last_completed_record:
|
if next_pending:
|
||||||
return last_completed_record.state
|
return next_pending['state']
|
||||||
|
|
||||||
# 没有任何完成记录,返回 None(未开始)
|
# 所有节点都已完成,返回 None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -239,13 +236,13 @@ def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple
|
|||||||
|
|
||||||
def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]:
|
def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]:
|
||||||
"""
|
"""
|
||||||
获取业务对象状态时间线(包括未开始和已完成的状态)
|
获取业务对象状态时间线(包括未开始、进行中和已完成的状态)
|
||||||
|
|
||||||
返回格式:
|
返回格式:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
'state': State对象,
|
'state': State对象,
|
||||||
'status': 'not_started' | 'completed' | 'cancelled',
|
'status': 'not_started' | 'in_progress' | 'completed' | 'cancelled',
|
||||||
'order': 顺序号,
|
'order': 顺序号,
|
||||||
'completed_at': 完成时间(如果已完成),
|
'completed_at': 完成时间(如果已完成),
|
||||||
'completed_by': 完成人(如果已完成),
|
'completed_by': 完成人(如果已完成),
|
||||||
@@ -254,6 +251,12 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
|||||||
},
|
},
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
|
||||||
|
状态判断规则:
|
||||||
|
- cancelled: 有日志记录且已撤销
|
||||||
|
- completed: 有日志记录且未撤销
|
||||||
|
- in_progress: 没有日志记录,但是 current_state(下一个待执行的节点),且已有完成记录
|
||||||
|
- not_started: 其他未开始的节点
|
||||||
"""
|
"""
|
||||||
timeline = []
|
timeline = []
|
||||||
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
|
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
|
||||||
@@ -264,6 +267,13 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
|||||||
for log in business_object.state_logs.select_related('completed_by')
|
for log in business_object.state_logs.select_related('completed_by')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 获取 current_state(下一个待执行的节点)
|
||||||
|
current_state = get_business_object_current_state(business_object)
|
||||||
|
current_state_id = current_state.id if current_state else None
|
||||||
|
|
||||||
|
# 检查是否有任何完成记录
|
||||||
|
has_any_completed = business_object.state_logs.filter(is_cancelled=False).exists()
|
||||||
|
|
||||||
for node in process_nodes:
|
for node in process_nodes:
|
||||||
state = node.state
|
state = node.state
|
||||||
log = state_logs_map.get(state.id)
|
log = state_logs_map.get(state.id)
|
||||||
@@ -277,6 +287,11 @@ def get_business_object_state_timeline(business_object: 'models.BusinessObject')
|
|||||||
completed_by = log.completed_by
|
completed_by = log.completed_by
|
||||||
cancelled_at = log.cancelled_at
|
cancelled_at = log.cancelled_at
|
||||||
is_cancelled = log.is_cancelled
|
is_cancelled = log.is_cancelled
|
||||||
|
else:
|
||||||
|
# 未开始的节点,判断是 in_progress 还是 not_started
|
||||||
|
# 如果是 current_state 且有任何完成记录,则为 in_progress
|
||||||
|
if has_any_completed and state.id == current_state_id:
|
||||||
|
status = 'in_progress'
|
||||||
else:
|
else:
|
||||||
status = 'not_started'
|
status = 'not_started'
|
||||||
completed_at = None
|
completed_at = None
|
||||||
|
|||||||
@@ -565,48 +565,30 @@ class BusinessObjectNewAPITestCase(TestCase):
|
|||||||
self.assertEqual(response.data['count'], 0)
|
self.assertEqual(response.data['count'], 0)
|
||||||
|
|
||||||
def test_current_state_parameters_api(self):
|
def test_current_state_parameters_api(self):
|
||||||
"""测试获取当前状态(最后完成状态)参数 API"""
|
"""测试获取当前状态(下一个待执行状态)参数 API"""
|
||||||
# 未开始时,current_state 为 None,应返回无参数消息
|
# 未开始时,current_state 为 state1(第一个待执行的),应返回 state1 的参数
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/'
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/'
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(len(response.data['parameters']), 0)
|
self.assertEqual(response.data['state']['id'], self.state1.id)
|
||||||
self.assertIn('message', response.data)
|
self.assertEqual(len(response.data['parameters']), 2)
|
||||||
|
|
||||||
# 推进(完成第一个状态),current_state 变为 state1(最后完成的状态)
|
# 推进(完成第一个状态),current_state 变为 state2(下一个待执行的状态)
|
||||||
self.client.post(
|
self.client.post(
|
||||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||||
{'parameters': {'param1': 'test_value'}},
|
{'parameters': {'param1': 'test_value'}},
|
||||||
format='json'
|
format='json'
|
||||||
)
|
)
|
||||||
|
|
||||||
# 现在 current_state 是 state1(有参数)
|
# 现在 current_state 是 state2(无参数)
|
||||||
response = self.client.get(
|
|
||||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/'
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertEqual(response.data['state']['id'], self.state1.id)
|
|
||||||
self.assertEqual(response.data['count'], 2)
|
|
||||||
self.assertEqual(len(response.data['parameters']), 2)
|
|
||||||
|
|
||||||
# 测试只获取必填参数
|
|
||||||
response = self.client.get(
|
|
||||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/?required_only=true'
|
|
||||||
)
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertEqual(response.data['count'], 1)
|
|
||||||
self.assertEqual(response.data['parameters'][0]['key'], 'param1')
|
|
||||||
|
|
||||||
# 推进(完成第二个状态),current_state 变为 state2(无参数)
|
|
||||||
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
|
||||||
|
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/'
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/current_state_parameters/'
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data['state']['id'], self.state2.id)
|
self.assertEqual(response.data['state']['id'], self.state2.id)
|
||||||
self.assertEqual(response.data['count'], 0)
|
self.assertEqual(response.data['count'], 0)
|
||||||
|
self.assertEqual(len(response.data['parameters']), 0)
|
||||||
|
|
||||||
def test_state_parameters_api(self):
|
def test_state_parameters_api(self):
|
||||||
"""测试 State 的参数 API"""
|
"""测试 State 的参数 API"""
|
||||||
|
|||||||
@@ -73,9 +73,10 @@ class BusinessObjectAPITestCase(TestCase):
|
|||||||
self.assertTrue(response.data['success'])
|
self.assertTrue(response.data['success'])
|
||||||
self.assertIn('重置', response.data['message'])
|
self.assertIn('重置', response.data['message'])
|
||||||
|
|
||||||
# 验证进度已重置
|
# 验证进度已重置(current_state 返回第一个待执行的节点)
|
||||||
self.business_object.refresh_from_db()
|
self.business_object.refresh_from_db()
|
||||||
self.assertIsNone(services.get_business_object_current_state(self.business_object))
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
|
self.assertEqual(current_state.name, self.state1.name)
|
||||||
self.assertEqual(self.business_object.state_logs.filter(is_cancelled=False).count(), 0)
|
self.assertEqual(self.business_object.state_logs.filter(is_cancelled=False).count(), 0)
|
||||||
|
|
||||||
def test_reset_api_not_started(self):
|
def test_reset_api_not_started(self):
|
||||||
@@ -120,8 +121,8 @@ class BusinessObjectAPITestCase(TestCase):
|
|||||||
self.assertEqual(response.data[0]['completed_by'], 'testuser')
|
self.assertEqual(response.data[0]['completed_by'], 'testuser')
|
||||||
self.assertFalse(response.data[0]['is_cancelled'])
|
self.assertFalse(response.data[0]['is_cancelled'])
|
||||||
|
|
||||||
# 其他状态仍是待执行
|
# 第二个状态是进行中(下一个待执行的)
|
||||||
self.assertEqual(response.data[1]['status'], 'not_started')
|
self.assertEqual(response.data[1]['status'], 'in_progress')
|
||||||
self.assertEqual(response.data[2]['status'], 'not_started')
|
self.assertEqual(response.data[2]['status'], 'not_started')
|
||||||
|
|
||||||
def test_timeline_api_with_step_back(self):
|
def test_timeline_api_with_step_back(self):
|
||||||
@@ -469,3 +470,59 @@ class BusinessObjectAPITestCase(TestCase):
|
|||||||
self.assertIsNotNone(item['completed_at'])
|
self.assertIsNotNone(item['completed_at'])
|
||||||
self.assertIsNotNone(item['completed_by'])
|
self.assertIsNotNone(item['completed_by'])
|
||||||
self.assertFalse(item['is_cancelled'])
|
self.assertFalse(item['is_cancelled'])
|
||||||
|
|
||||||
|
def test_advance_when_already_completed(self):
|
||||||
|
"""测试流程已完成时尝试推进(预期错误)"""
|
||||||
|
# 完成所有状态
|
||||||
|
self.client.post(
|
||||||
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/',
|
||||||
|
{'parameters': {'temperature': '25.5'}},
|
||||||
|
format='json'
|
||||||
|
)
|
||||||
|
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||||
|
self.client.post(f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/')
|
||||||
|
|
||||||
|
# 验证已完成
|
||||||
|
self.assertEqual(services.get_overall_status(self.business_object), 'completed')
|
||||||
|
|
||||||
|
# 尝试再次推进(应该失败)
|
||||||
|
response = self.client.post(
|
||||||
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/advance/'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(response.data['success'])
|
||||||
|
self.assertIn('流程已完成', response.data['message'])
|
||||||
|
self.assertIn('无法继续推进', response.data['message'])
|
||||||
|
|
||||||
|
def test_step_back_when_not_started(self):
|
||||||
|
"""测试未开始时尝试回退(预期错误)"""
|
||||||
|
# 验证未开始
|
||||||
|
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||||
|
|
||||||
|
# 尝试回退(应该失败)
|
||||||
|
response = self.client.post(
|
||||||
|
f'/api/v1/stateflow/business-objects/{self.business_object.id}/step_back/'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(response.data['success'])
|
||||||
|
self.assertIn('没有任何状态流转记录', response.data['message'])
|
||||||
|
|
||||||
|
def test_advance_process_without_nodes(self):
|
||||||
|
"""测试流程没有节点时尝试推进(预期错误)"""
|
||||||
|
# 创建一个没有节点的流程
|
||||||
|
empty_process = models.Process.objects.create(name='空流程')
|
||||||
|
empty_business_object = models.BusinessObject.objects.create(
|
||||||
|
name='空业务对象',
|
||||||
|
process=empty_process
|
||||||
|
)
|
||||||
|
|
||||||
|
# 尝试推进(应该失败)
|
||||||
|
response = self.client.post(
|
||||||
|
f'/api/v1/stateflow/business-objects/{empty_business_object.id}/advance/'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertFalse(response.data['success'])
|
||||||
|
self.assertIn('流程没有任何节点', response.data['message'])
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ class ParameterManagementTestCase(TestCase):
|
|||||||
self.assertIn('temperature', message)
|
self.assertIn('temperature', message)
|
||||||
self.assertIsNone(state_log)
|
self.assertIsNone(state_log)
|
||||||
|
|
||||||
# 验证状态未改变
|
# 验证状态未改变(仍然是第一个待执行的节点)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertIsNone(current_state)
|
self.assertEqual(current_state.name, self.state1.name)
|
||||||
|
|
||||||
# 验证没有创建状态流转记录
|
# 验证没有创建状态流转记录
|
||||||
self.assertEqual(self.business_object.state_logs.count(), 0)
|
self.assertEqual(self.business_object.state_logs.count(), 0)
|
||||||
@@ -92,9 +92,9 @@ class ParameterManagementTestCase(TestCase):
|
|||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
self.assertIsNotNone(state_log)
|
self.assertIsNotNone(state_log)
|
||||||
|
|
||||||
# 验证状态已推进
|
# 验证状态已推进(下一个待执行的是 state2)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state1.name)
|
self.assertEqual(current_state.name, self.state2.name)
|
||||||
|
|
||||||
# 验证参数记录已创建
|
# 验证参数记录已创建
|
||||||
self.assertEqual(state_log.parameter_records.count(), 1)
|
self.assertEqual(state_log.parameter_records.count(), 1)
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ class StateFlowServicesTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_initial_state(self):
|
def test_initial_state(self):
|
||||||
"""测试初始状态 - 应该是 None(未开始)"""
|
"""测试初始状态 - 应该返回第一个待执行的节点"""
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
self.assertIsNone(current_state, '初始状态应该是 None(未开始)')
|
self.assertEqual(current_state.name, self.state1.name, '初始状态应该返回第一个待执行的节点')
|
||||||
self.assertEqual(self.business_object.get_progress_percentage(), 0.0)
|
self.assertEqual(self.business_object.get_progress_percentage(), 0.0)
|
||||||
|
|
||||||
# 整体状态应该是 not_started
|
# 整体状态应该是 not_started
|
||||||
@@ -57,18 +57,18 @@ class StateFlowServicesTestCase(TestCase):
|
|||||||
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
|
|
||||||
# current_state 应该是最后完成的状态(state1)
|
# current_state 应该是下一个待执行的状态(state2)
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
self.assertEqual(current_state.name, self.state1.name)
|
self.assertEqual(current_state.name, self.state2.name)
|
||||||
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 33.33, places=1)
|
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 33.33, places=1)
|
||||||
|
|
||||||
# 推进到状态2(完成state2)
|
# 推进到状态2(完成state2)
|
||||||
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
success, message, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
|
|
||||||
# current_state 应该是 state2
|
# current_state 应该是 state3(下一个待执行的)
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
self.assertEqual(current_state.name, self.state2.name)
|
self.assertEqual(current_state.name, self.state3.name)
|
||||||
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 66.67, places=1)
|
self.assertAlmostEqual(self.business_object.get_progress_percentage(), 66.67, places=1)
|
||||||
|
|
||||||
# 完成最后一个状态(state3)
|
# 完成最后一个状态(state3)
|
||||||
@@ -76,9 +76,9 @@ class StateFlowServicesTestCase(TestCase):
|
|||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
self.assertEqual(self.business_object.get_progress_percentage(), 100.0)
|
self.assertEqual(self.business_object.get_progress_percentage(), 100.0)
|
||||||
|
|
||||||
# current_state 应该是 state3(最后完成的状态)
|
# current_state 应该是 None(没有待执行的节点了)
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
self.assertEqual(current_state.name, self.state3.name)
|
self.assertIsNone(current_state)
|
||||||
|
|
||||||
# 整体状态应该是 completed
|
# 整体状态应该是 completed
|
||||||
status = services.get_overall_status(self.business_object)
|
status = services.get_overall_status(self.business_object)
|
||||||
@@ -116,7 +116,7 @@ class StateFlowServicesTestCase(TestCase):
|
|||||||
|
|
||||||
timeline = self.business_object.get_timeline()
|
timeline = self.business_object.get_timeline()
|
||||||
self.assertEqual(timeline[0]['status'], 'completed')
|
self.assertEqual(timeline[0]['status'], 'completed')
|
||||||
self.assertEqual(timeline[1]['status'], 'not_started')
|
self.assertEqual(timeline[1]['status'], 'in_progress') # 下一个待执行的节点
|
||||||
self.assertEqual(timeline[2]['status'], 'not_started')
|
self.assertEqual(timeline[2]['status'], 'not_started')
|
||||||
self.assertIsNotNone(timeline[0]['completed_by'])
|
self.assertIsNotNone(timeline[0]['completed_by'])
|
||||||
self.assertEqual(timeline[0]['completed_by'].id, self.user.id)
|
self.assertEqual(timeline[0]['completed_by'].id, self.user.id)
|
||||||
@@ -141,9 +141,9 @@ class StateFlowServicesTestCase(TestCase):
|
|||||||
cancelled_count = self.business_object.state_logs.filter(is_cancelled=True).count()
|
cancelled_count = self.business_object.state_logs.filter(is_cancelled=True).count()
|
||||||
self.assertEqual(cancelled_count, 2)
|
self.assertEqual(cancelled_count, 2)
|
||||||
|
|
||||||
# 应该回到初始状态(未开始)
|
# 应该回到初始状态(返回第一个待执行的节点)
|
||||||
current_state = self.business_object.get_current_state()
|
current_state = self.business_object.get_current_state()
|
||||||
self.assertIsNone(current_state)
|
self.assertEqual(current_state.name, self.state1.name)
|
||||||
|
|
||||||
# 所有撤销的记录都应该有撤销时间
|
# 所有撤销的记录都应该有撤销时间
|
||||||
for log in self.business_object.state_logs.all():
|
for log in self.business_object.state_logs.all():
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ class StepBackTestCase(TestCase):
|
|||||||
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
|
|
||||||
# 验证 current_state 是 state1(最后完成的状态)
|
# 验证 current_state 是 state2(下一个待执行的状态)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state1.name)
|
self.assertEqual(current_state.name, self.state2.name)
|
||||||
|
|
||||||
# 回退一步
|
# 回退一步
|
||||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||||
@@ -63,7 +63,7 @@ class StepBackTestCase(TestCase):
|
|||||||
# 验证回到未开始状态
|
# 验证回到未开始状态
|
||||||
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
self.assertEqual(services.get_overall_status(self.business_object), 'not_started')
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertIsNone(current_state)
|
self.assertEqual(current_state.name, self.state1.name)
|
||||||
|
|
||||||
# 验证状态1的记录已被撤销
|
# 验证状态1的记录已被撤销
|
||||||
record = models.StateFlowRecord.objects.filter(
|
record = models.StateFlowRecord.objects.filter(
|
||||||
@@ -79,18 +79,18 @@ class StepBackTestCase(TestCase):
|
|||||||
services.advance_to_next_state(self.business_object, self.user)
|
services.advance_to_next_state(self.business_object, self.user)
|
||||||
services.advance_to_next_state(self.business_object, self.user)
|
services.advance_to_next_state(self.business_object, self.user)
|
||||||
|
|
||||||
# 验证 current_state 是 state2(最后完成的状态)
|
# 验证 current_state 是 state3(下一个待执行的状态)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state2.name)
|
self.assertEqual(current_state.name, self.state3.name)
|
||||||
|
|
||||||
# 回退一步
|
# 回退一步
|
||||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
self.assertIn('状态2', message)
|
self.assertIn('状态2', message)
|
||||||
|
|
||||||
# 验证 current_state 变为state1
|
# 验证 current_state 变为 state2(下一个待执行的)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state1.name)
|
self.assertEqual(current_state.name, self.state2.name)
|
||||||
|
|
||||||
# 验证状态2的记录已被撤销
|
# 验证状态2的记录已被撤销
|
||||||
record = models.StateFlowRecord.objects.filter(
|
record = models.StateFlowRecord.objects.filter(
|
||||||
@@ -106,20 +106,20 @@ class StepBackTestCase(TestCase):
|
|||||||
services.advance_to_next_state(self.business_object, self.user)
|
services.advance_to_next_state(self.business_object, self.user)
|
||||||
services.advance_to_next_state(self.business_object, self.user)
|
services.advance_to_next_state(self.business_object, self.user)
|
||||||
|
|
||||||
# 验证已完成,current_state 是 state3
|
# 验证已完成,current_state 是 None(没有待执行的节点)
|
||||||
self.assertEqual(services.get_overall_status(self.business_object), 'completed')
|
self.assertEqual(services.get_overall_status(self.business_object), 'completed')
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state3.name)
|
self.assertIsNone(current_state)
|
||||||
|
|
||||||
# 回退一步
|
# 回退一步
|
||||||
success, message = services.step_back_one_state(self.business_object, self.user)
|
success, message = services.step_back_one_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
self.assertIn('状态3', message)
|
self.assertIn('状态3', message)
|
||||||
|
|
||||||
# 验证回到进行中状态,current_state 变为state2
|
# 验证回到进行中状态,current_state 变为 state3(下一个待执行的)
|
||||||
self.assertEqual(services.get_overall_status(self.business_object), 'in_progress')
|
self.assertEqual(services.get_overall_status(self.business_object), 'in_progress')
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state2.name)
|
self.assertEqual(current_state.name, self.state3.name)
|
||||||
|
|
||||||
def test_step_back_multiple_times(self):
|
def test_step_back_multiple_times(self):
|
||||||
"""测试多次回退"""
|
"""测试多次回退"""
|
||||||
@@ -132,13 +132,13 @@ class StepBackTestCase(TestCase):
|
|||||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
current = services.get_business_object_current_state(self.business_object)
|
current = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current.name, self.state2.name)
|
self.assertEqual(current.name, self.state3.name)
|
||||||
|
|
||||||
# 第二次回退(撤销 state2)
|
# 第二次回退(撤销 state2)
|
||||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
current = services.get_business_object_current_state(self.business_object)
|
current = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current.name, self.state1.name)
|
self.assertEqual(current.name, self.state2.name) # 下一个待执行的是 state2
|
||||||
|
|
||||||
# 第三次回退
|
# 第三次回退
|
||||||
success, _ = services.step_back_one_state(self.business_object, self.user)
|
success, _ = services.step_back_one_state(self.business_object, self.user)
|
||||||
@@ -163,9 +163,9 @@ class StepBackTestCase(TestCase):
|
|||||||
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
success, _, _ = services.advance_to_next_state(self.business_object, self.user)
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
|
|
||||||
# 验证 current_state 是 state2(最后完成的)
|
# 验证 current_state 是 state3(下一个待执行的)
|
||||||
current_state = services.get_business_object_current_state(self.business_object)
|
current_state = services.get_business_object_current_state(self.business_object)
|
||||||
self.assertEqual(current_state.name, self.state2.name)
|
self.assertEqual(current_state.name, self.state3.name)
|
||||||
|
|
||||||
def test_step_back_preserves_history(self):
|
def test_step_back_preserves_history(self):
|
||||||
"""测试回退操作保留历史记录"""
|
"""测试回退操作保留历史记录"""
|
||||||
|
|||||||
1
test_output.log
Normal file
1
test_output.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
zsh: 没有那个文件或目录: home/f/coding/flower/.venv/bin/python
|
||||||
Reference in New Issue
Block a user