forked from erp-dev/erp
276 lines
9.5 KiB
Python
276 lines
9.5 KiB
Python
"""
|
||
Stateflow业务逻辑服务层
|
||
"""
|
||
from typing import List, Optional, Tuple
|
||
from django.contrib.auth import get_user_model
|
||
from django.db import transaction
|
||
from . import models
|
||
|
||
User = get_user_model()
|
||
|
||
|
||
def get_business_object_current_state(business_object: 'models.BusinessObject') -> Optional['models.State']:
|
||
"""
|
||
获取订单的当前状态
|
||
|
||
规则:
|
||
1. 如果没有任何完成记录(或所有记录都被撤销),返回 None(未开始处理)
|
||
2. 如果有未完成的节点,返回第一个未完成的节点(进行中)
|
||
3. 如果所有节点都已完成,返回 None(流程已完成)
|
||
"""
|
||
# 获取流程的所有节点(按顺序)
|
||
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
|
||
if not process_nodes.exists():
|
||
return None
|
||
|
||
# 获取已完成且未被撤销的状态ID集合
|
||
completed_state_ids = set(
|
||
business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True)
|
||
)
|
||
|
||
# 如果没有任何有效完成记录,返回 None(未开始)
|
||
if not completed_state_ids:
|
||
return None
|
||
|
||
# 找到第一个未完成的节点
|
||
for node in process_nodes:
|
||
if node.state_id not in completed_state_ids:
|
||
return node.state
|
||
|
||
# 所有节点都已完成,返回 None(已完成)
|
||
return None
|
||
|
||
|
||
def get_business_object_state_status(business_object: 'models.BusinessObject', state: 'models.State') -> str:
|
||
"""
|
||
获取订单中某个状态的状态
|
||
|
||
返回值:
|
||
- 'not_started': 未开始
|
||
- 'in_progress': 进行中
|
||
- 'completed': 已完成
|
||
"""
|
||
# 检查是否已完成且未被撤销
|
||
is_completed = business_object.state_logs.filter(state=state, is_cancelled=False).exists()
|
||
if is_completed:
|
||
return 'completed'
|
||
|
||
current_state = get_business_object_current_state(business_object)
|
||
|
||
# 检查是否为当前状态
|
||
if current_state and current_state.id == state.id:
|
||
return 'in_progress'
|
||
|
||
return 'not_started'
|
||
|
||
|
||
def get_completed_node_ids(business_object: 'models.BusinessObject') -> List[int]:
|
||
"""获取订单已完成且未撤销的节点ID列表(按完成顺序)"""
|
||
return list(
|
||
business_object.state_logs.filter(is_cancelled=False)
|
||
.order_by('completed_at')
|
||
.values_list('state_id', flat=True)
|
||
)
|
||
|
||
|
||
def get_progress_percentage(business_object: 'models.BusinessObject') -> float:
|
||
"""计算订单进度百分比"""
|
||
total_nodes = business_object.process.process_nodes.count()
|
||
if total_nodes == 0:
|
||
return 0.0
|
||
|
||
completed_count = business_object.state_logs.filter(is_cancelled=False).count()
|
||
return (completed_count / total_nodes) * 100
|
||
|
||
|
||
def advance_to_next_state(business_object: 'models.BusinessObject', user: 'User') -> Tuple[bool, str]:
|
||
"""
|
||
将订单推进到下一个状态
|
||
|
||
返回: (是否成功, 消息)
|
||
"""
|
||
with transaction.atomic():
|
||
# 获取当前状态
|
||
current_state = get_business_object_current_state(business_object)
|
||
|
||
# 如果当前状态为 None,说明是初始状态(未开始)
|
||
if current_state is None:
|
||
# 获取第一个节点
|
||
first_node = business_object.process.process_nodes.order_by('order', 'id').first()
|
||
if not first_node:
|
||
return False, "流程没有任何节点"
|
||
|
||
# 标记第一个状态为已完成
|
||
models.StateFlowRecord.objects.create(
|
||
business_object=business_object,
|
||
state=first_node.state,
|
||
completed_by=user
|
||
)
|
||
return True, f"已完成状态: {first_node.state.name}"
|
||
|
||
# 检查当前状态是否已完成(且未撤销)
|
||
if business_object.state_logs.filter(state=current_state, is_cancelled=False).exists():
|
||
return False, f"当前状态 '{current_state.name}' 已完成"
|
||
|
||
# 标记当前状态为已完成
|
||
models.StateFlowRecord.objects.create(
|
||
business_object=business_object,
|
||
state=current_state,
|
||
completed_by=user
|
||
)
|
||
|
||
# 检查是否所有状态都已完成
|
||
next_state = get_business_object_current_state(business_object)
|
||
if next_state is None:
|
||
# 所有状态都已完成
|
||
return True, f"流程已完成,最后状态: {current_state.name}"
|
||
|
||
return True, f"已完成状态: {current_state.name}"
|
||
|
||
|
||
def reset_order_progress(business_object: 'models.BusinessObject') -> None:
|
||
"""
|
||
重置订单进度
|
||
|
||
注意:不删除历史记录,而是标记所有记录为已撤销,并记录撤销时间
|
||
这样可以保留完整的操作历史
|
||
"""
|
||
from django.utils import timezone
|
||
|
||
business_object.state_logs.filter(is_cancelled=False).update(
|
||
is_cancelled=True,
|
||
cancelled_at=timezone.now()
|
||
)
|
||
|
||
|
||
def get_current_state_parameters(business_object: 'models.BusinessObject') -> List['models.StateParameter']:
|
||
"""获取订单当前状态的参数列表"""
|
||
current_state = get_business_object_current_state(business_object)
|
||
if current_state:
|
||
return list(current_state.parameters.all())
|
||
return []
|
||
|
||
|
||
def get_overall_status(business_object: 'models.BusinessObject') -> str:
|
||
"""
|
||
获取订单的整体状态
|
||
|
||
返回值:
|
||
- 'not_started': 未开始(没有任何有效的完成记录)
|
||
- 'in_progress': 进行中(有部分状态已完成)
|
||
- 'completed': 已完成(所有状态都已完成)
|
||
"""
|
||
current_state = get_business_object_current_state(business_object)
|
||
|
||
# 检查是否有任何有效的完成记录
|
||
has_completed = business_object.state_logs.filter(is_cancelled=False).exists()
|
||
|
||
if not has_completed:
|
||
return 'not_started'
|
||
|
||
if current_state is None:
|
||
# 有完成记录,但当前状态为 None,说明所有状态都已完成
|
||
return 'completed'
|
||
|
||
return 'in_progress'
|
||
|
||
|
||
def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple[bool, str]:
|
||
"""
|
||
检查订单是否可以推进到下一个状态
|
||
|
||
返回: (是否可以推进, 原因)
|
||
"""
|
||
# 检查流程是否有节点
|
||
first_node = business_object.process.process_nodes.order_by('order', 'id').first()
|
||
if not first_node:
|
||
return False, "流程没有任何节点"
|
||
|
||
current_state = get_business_object_current_state(business_object)
|
||
|
||
# 如果当前状态为 None
|
||
if current_state is None:
|
||
# 检查是否所有状态都已完成
|
||
total_nodes = business_object.process.process_nodes.count()
|
||
completed_count = business_object.state_logs.filter(is_cancelled=False).count()
|
||
|
||
if completed_count == 0:
|
||
# 未开始,可以推进到第一个状态
|
||
return True, f"可以开始处理,将推进到: {first_node.state.name}"
|
||
else:
|
||
# 所有状态都已完成
|
||
return False, "流程已完成,无法继续推进"
|
||
|
||
# 检查当前状态是否已完成
|
||
if business_object.state_logs.filter(state=current_state, is_cancelled=False).exists():
|
||
return False, f"当前状态 '{current_state.name}' 已完成,无法重复完成"
|
||
|
||
return True, f"可以推进到: {current_state.name}"
|
||
|
||
|
||
def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]:
|
||
"""
|
||
获取订单状态时间线(包括未开始、进行中和已完成的状态)
|
||
|
||
返回格式:
|
||
[
|
||
{
|
||
'state': State对象,
|
||
'status': 'not_started' | 'in_progress' | 'completed' | 'cancelled',
|
||
'business_object': 顺序号,
|
||
'completed_at': 完成时间(如果已完成),
|
||
'completed_by': 完成人(如果已完成),
|
||
'cancelled_at': 撤销时间(如果已撤销),
|
||
'is_cancelled': 是否已撤销,
|
||
},
|
||
...
|
||
]
|
||
"""
|
||
timeline = []
|
||
process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
|
||
current_state = get_business_object_current_state(business_object)
|
||
|
||
# 构建状态日志映射(包括已撤销的记录)
|
||
state_logs_map = {
|
||
log.state_id: log
|
||
for log in business_object.state_logs.select_related('completed_by')
|
||
}
|
||
|
||
for node in process_nodes:
|
||
state = node.state
|
||
log = state_logs_map.get(state.id)
|
||
|
||
if log:
|
||
if log.is_cancelled:
|
||
status = 'cancelled'
|
||
else:
|
||
status = 'completed'
|
||
completed_at = log.completed_at
|
||
completed_by = log.completed_by
|
||
cancelled_at = log.cancelled_at
|
||
is_cancelled = log.is_cancelled
|
||
elif current_state and state.id == current_state.id:
|
||
status = 'in_progress'
|
||
completed_at = None
|
||
completed_by = None
|
||
cancelled_at = None
|
||
is_cancelled = False
|
||
else:
|
||
status = 'not_started'
|
||
completed_at = None
|
||
completed_by = None
|
||
cancelled_at = None
|
||
is_cancelled = False
|
||
|
||
timeline.append({
|
||
'state': state,
|
||
'status': status,
|
||
'order': node.order,
|
||
'completed_at': completed_at,
|
||
'completed_by': completed_by,
|
||
'cancelled_at': cancelled_at,
|
||
'is_cancelled': is_cancelled,
|
||
})
|
||
|
||
return timeline
|