845 lines
35 KiB
Python
845 lines
35 KiB
Python
"""
|
||
审批相关的工具函数
|
||
"""
|
||
import json
|
||
from datetime import datetime
|
||
from .models import OperationLog, User, Team, Approval, Department
|
||
|
||
def is_department_id(value):
|
||
"""
|
||
判断personincharge字段的值是部门ID还是审批员用户名
|
||
|
||
统一规则:
|
||
- 如果是纯数字字符串(如 "1", "2", "123"),表示部门ID
|
||
- 如果包含非数字字符(如 "张三", "李四"),表示审批员用户名
|
||
|
||
Args:
|
||
value: personincharge字段的值(字符串)
|
||
|
||
Returns:
|
||
bool: True表示是部门ID,False表示是审批员用户名
|
||
|
||
示例:
|
||
>>> is_department_id("1")
|
||
True
|
||
>>> is_department_id("123")
|
||
True
|
||
>>> is_department_id("张三")
|
||
False
|
||
>>> is_department_id("dept:1")
|
||
False
|
||
"""
|
||
if not value:
|
||
return False
|
||
# 判断是否为纯数字字符串(去除首尾空格)
|
||
return str(value).strip().isdigit()
|
||
|
||
|
||
def format_personincharge(value, is_department=False):
|
||
"""
|
||
格式化personincharge字段的值
|
||
|
||
统一规则:
|
||
- 如果是部门ID,确保是纯数字字符串
|
||
- 如果是审批员用户名,保持原样
|
||
|
||
Args:
|
||
value: 部门ID(整数或字符串)或审批员用户名(字符串)
|
||
is_department: 是否为部门ID,默认False(审批员用户名)
|
||
|
||
Returns:
|
||
str: 格式化后的personincharge值
|
||
"""
|
||
if not value:
|
||
return ''
|
||
|
||
if is_department:
|
||
# 部门ID:转换为字符串,确保是纯数字
|
||
try:
|
||
return str(int(value))
|
||
except (ValueError, TypeError):
|
||
raise ValueError(f"部门ID必须是数字,当前值: {value}")
|
||
else:
|
||
# 审批员用户名:保持原样,但确保是字符串
|
||
return str(value).strip()
|
||
|
||
|
||
def get_finance_personincharge_candidates():
|
||
"""
|
||
获取财务部抄送的负责人标识列表,优先使用部门ID,其次回退字符串“财务”。
|
||
返回按优先级去重的字符串列表,便于 personincharge 匹配和查询。
|
||
"""
|
||
candidates = []
|
||
try:
|
||
finance_dept_ids = Department.objects.filter(
|
||
is_deleted=False,
|
||
username__icontains="财务"
|
||
).values_list("id", flat=True)
|
||
candidates.extend([str(dept_id) for dept_id in finance_dept_ids])
|
||
except Exception:
|
||
pass
|
||
|
||
# 回退保留原有字符串标识,兼容历史数据
|
||
candidates.append("财务")
|
||
|
||
# 去重保持顺序
|
||
uniq = []
|
||
seen = set()
|
||
for item in candidates:
|
||
if item is None:
|
||
continue
|
||
item_str = str(item)
|
||
if item_str not in seen:
|
||
uniq.append(item_str)
|
||
seen.add(item_str)
|
||
return uniq
|
||
|
||
|
||
def get_finance_personincharge_value():
|
||
"""获取优先的财务抄送标识(用于写入 personincharge)。"""
|
||
candidates = get_finance_personincharge_candidates()
|
||
return candidates[0] if candidates else "财务"
|
||
|
||
|
||
def is_finance_personincharge(value):
|
||
"""判断 personincharge 是否表示财务部门抄送(部门ID或“财务”字符串)。"""
|
||
if value is None:
|
||
return False
|
||
return str(value) in get_finance_personincharge_candidates()
|
||
|
||
|
||
def log_operation(request, operation_type, module, action, target_type, target_id=None,
|
||
target_name=None, old_data=None, new_data=None, remark=None):
|
||
"""
|
||
记录操作日志
|
||
|
||
Args:
|
||
request: Django request对象
|
||
operation_type: 操作类型(DELETE, CREATE, UPDATE, APPROVE等)
|
||
module: 模块名称(User, Business, Finance等)
|
||
action: 操作描述(如"删除用户"、"创建立项"等)
|
||
target_type: 目标类型(如User, ProjectRegistration等)
|
||
target_id: 目标ID
|
||
target_name: 目标名称(如用户名、项目名等)
|
||
old_data: 操作前的数据(字典,会自动转换为JSON)
|
||
new_data: 操作后的数据(字典,会自动转换为JSON)
|
||
remark: 备注信息
|
||
|
||
Returns:
|
||
OperationLog对象
|
||
"""
|
||
try:
|
||
# 获取操作人信息
|
||
token = request.META.get('token') or request.META.get('HTTP_AUTHORIZATION', '').replace('Bearer ', '')
|
||
operator = '未知用户'
|
||
operator_id = None
|
||
|
||
if token:
|
||
try:
|
||
user = User.objects.get(token=token, is_deleted=False)
|
||
operator = user.username
|
||
operator_id = user.id
|
||
except User.DoesNotExist:
|
||
pass
|
||
|
||
# 获取IP地址
|
||
ip_address = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')[0].strip()
|
||
if not ip_address:
|
||
ip_address = request.META.get('REMOTE_ADDR', '')
|
||
|
||
# 获取用户代理
|
||
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
||
|
||
# 转换数据为JSON字符串
|
||
old_data_str = json.dumps(old_data, ensure_ascii=False) if old_data else None
|
||
new_data_str = json.dumps(new_data, ensure_ascii=False) if new_data else None
|
||
|
||
# 创建日志记录
|
||
log = OperationLog.objects.create(
|
||
operator=operator,
|
||
operator_id=operator_id,
|
||
operation_type=operation_type,
|
||
module=module,
|
||
action=action,
|
||
target_type=target_type,
|
||
target_id=str(target_id) if target_id else None,
|
||
target_name=target_name,
|
||
old_data=old_data_str,
|
||
new_data=new_data_str,
|
||
ip_address=ip_address,
|
||
user_agent=user_agent,
|
||
request_path=request.path,
|
||
remark=remark
|
||
)
|
||
return log
|
||
except Exception as e:
|
||
# 日志记录失败不应该影响主业务流程
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.error(f"记录操作日志失败: {str(e)}")
|
||
return None
|
||
|
||
|
||
def create_team_approval(team_name, title, content, approval_type, user_id, request=None):
|
||
"""
|
||
根据团队类型创建审批流程
|
||
|
||
规则:
|
||
- 个人团队(personal):不需要审核人,直接抄送财务(personincharge设为"财务")
|
||
- 团队(team):需要指定审核人(可以多个),审核通过后才抄送财务
|
||
|
||
Args:
|
||
team_name: 团队名称
|
||
title: 审批标题
|
||
content: 审批内容
|
||
approval_type: 审批类型(如"立项登记"、"付款申请"等)
|
||
user_id: 关联的业务ID(字符串)
|
||
request: Django request对象(可选,用于记录日志)
|
||
|
||
Returns:
|
||
Approval对象或None
|
||
"""
|
||
try:
|
||
# 查找团队
|
||
team = Team.objects.prefetch_related('approvers').filter(name=team_name, is_deleted=False).first()
|
||
|
||
if not team:
|
||
# 如果找不到团队,使用默认流程(需要审核人)
|
||
return None
|
||
|
||
today = datetime.now().strftime("%Y-%m-%d")
|
||
|
||
if team.team_type == 'personal':
|
||
# 个人团队:直接抄送财务,不需要审核人
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content,
|
||
times=today,
|
||
personincharge='财务', # 直接抄送财务
|
||
state='已抄送财务', # 直接标记为已抄送财务
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
return approval
|
||
|
||
elif team.team_type == 'team':
|
||
# 团队:需要审核人审核
|
||
approvers = team.approvers.filter(is_deleted=False)
|
||
if not approvers.exists():
|
||
# 如果没有审核人,返回None(应该在前端验证)
|
||
return None
|
||
|
||
# 将审核人用户名用逗号连接(多个审核人)
|
||
approver_names = ','.join([approver.username for approver in approvers])
|
||
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content,
|
||
times=today,
|
||
personincharge=approver_names, # 多个审核人用逗号分隔
|
||
state='审核中',
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
return approval
|
||
|
||
return None
|
||
|
||
except Exception as e:
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.error(f"创建团队审批失败: {str(e)}")
|
||
return None
|
||
|
||
|
||
def get_team_approval_info(team_name):
|
||
"""
|
||
获取团队的审批信息
|
||
|
||
Args:
|
||
team_name: 团队名称
|
||
|
||
Returns:
|
||
dict: {
|
||
'team_type': 'personal' 或 'team',
|
||
'needs_approval': True/False, # 是否需要审核
|
||
'approvers': [{'id': 1, 'username': '张三'}, ...], # 审核人列表
|
||
'direct_to_finance': True/False # 是否直接抄送财务
|
||
}
|
||
"""
|
||
try:
|
||
team = Team.objects.prefetch_related('approvers').filter(name=team_name, is_deleted=False).first()
|
||
|
||
if not team:
|
||
return {
|
||
'team_type': None,
|
||
'needs_approval': True, # 默认需要审核
|
||
'approvers': [],
|
||
'direct_to_finance': False
|
||
}
|
||
|
||
approvers = list(team.approvers.filter(is_deleted=False).values('id', 'username'))
|
||
|
||
return {
|
||
'team_type': team.team_type,
|
||
'needs_approval': team.team_type == 'team',
|
||
'approvers': approvers,
|
||
'direct_to_finance': team.team_type == 'personal'
|
||
}
|
||
|
||
except Exception as e:
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.error(f"获取团队审批信息失败: {str(e)}")
|
||
return {
|
||
'team_type': None,
|
||
'needs_approval': True,
|
||
'approvers': [],
|
||
'direct_to_finance': False
|
||
}
|
||
|
||
|
||
def normalize_approvers_param(approvers, personincharge):
|
||
"""
|
||
兼容旧接口:如果未传 approvers,则使用 personincharge。
|
||
personincharge 支持单个值、逗号分隔字符串或列表格式。
|
||
"""
|
||
if approvers:
|
||
return approvers
|
||
if personincharge:
|
||
return personincharge
|
||
return approvers
|
||
|
||
|
||
def build_missing_approvers_message(team_name, approvers):
|
||
"""
|
||
统一生成“缺少审核人”的详细错误文案
|
||
适用于团队类型需要审批但未能解析到有效审核人的情况
|
||
"""
|
||
try:
|
||
approvers_missing = approvers is None
|
||
approvers_empty = approvers == "" or approvers == []
|
||
approvers_provided = not approvers_missing and not approvers_empty
|
||
team = None
|
||
if team_name:
|
||
team = Team.objects.prefetch_related('approvers').filter(
|
||
name=team_name, is_deleted=False
|
||
).first()
|
||
|
||
if not team_name:
|
||
return "当前用户未绑定团队,无法自动获取审核人,请传 approvers 参数或先配置团队。"
|
||
|
||
if not team:
|
||
return f'团队"{team_name}"不存在或已被删除,请检查团队配置或传 approvers 参数。'
|
||
|
||
if team.team_type != "team":
|
||
return f'团队"{team_name}"为个人团队,无需审核人。'
|
||
|
||
if approvers_empty:
|
||
return "已传 approvers 但为空,请传入有效的审核人列表(推荐ID数组,如[1,2,3])。"
|
||
|
||
if approvers_provided:
|
||
approvers_list = parse_approvers(approvers)
|
||
if not approvers_list:
|
||
return "已传 approvers 但解析失败,请按推荐格式传入用户ID数组,如 [1,2,3]。"
|
||
invalid = [
|
||
name for name in approvers_list
|
||
if not User.objects.filter(username=name, is_deleted=False).exists()
|
||
]
|
||
if invalid and len(invalid) == len(approvers_list):
|
||
return f'已传 approvers,但全部审核人无效/不存在:{", ".join(invalid)}。请更换有效审核人或调整团队配置。'
|
||
if invalid:
|
||
return f'已传 approvers,但部分审核人无效:{", ".join(invalid)}。请移除无效人员或补充有效审核人。'
|
||
return "审核人参数已传入但审批创建失败,请检查团队配置或权限。"
|
||
|
||
# 未传 approvers,尝试使用团队配置
|
||
team_approvers = list(
|
||
team.approvers.filter(is_deleted=False).values_list("username", flat=True)
|
||
)
|
||
if not team_approvers:
|
||
return f'未传 approvers,且团队"{team_name}"未配置审核人,请先配置团队审核人或传 approvers 参数。'
|
||
|
||
valid_team_approvers = [
|
||
name for name in team_approvers
|
||
if User.objects.filter(username=name, is_deleted=False).exists()
|
||
]
|
||
if not valid_team_approvers:
|
||
return f'未传 approvers,且团队"{team_name}"配置的审核人无效或已被删除,请重新配置或传 approvers 参数。'
|
||
|
||
return (
|
||
f'未传 approvers。团队"{team_name}"为团队类型,需要审核人。团队当前审核人:'
|
||
f'{", ".join(valid_team_approvers)}。'
|
||
'请传 approvers 参数(推荐ID数组,如[1,2,3]),或调整团队审核人配置。'
|
||
)
|
||
except Exception:
|
||
return "团队类型需要指定审核人,请提供 approvers 参数并检查团队配置。"
|
||
|
||
|
||
def parse_approvers(approvers):
|
||
"""
|
||
解析审核人列表,统一处理数组和字符串格式
|
||
支持用户ID列表(推荐)和用户名列表(兼容旧接口)
|
||
|
||
Args:
|
||
approvers: 审核人列表,可以是:
|
||
- 数组格式(ID列表,推荐):[1, 2, 3] 或 ["1", "2", "3"]
|
||
- 数组格式(用户名列表,兼容):["张三", "李四", "王五"]
|
||
- JSON字符串格式(ID列表):"[1,2,3]" 或 "[5]" 或 '"[1,2,3]"'
|
||
- JSON字符串格式(带空格):"[ 5 ]" 或 "[1, 2, 3]"
|
||
- JSON字符串格式(用户名列表,兼容):'["张三","李四","王五"]'
|
||
- 字符串格式(逗号分隔的ID):"1,2,3" 或 "5"
|
||
- 字符串格式(逗号分隔的用户名,兼容):"张三,李四,王五"
|
||
- None: 返回空列表
|
||
|
||
Returns:
|
||
list: 审核人用户名列表,如 ["张三", "李四", "王五"]
|
||
"""
|
||
import json
|
||
|
||
if not approvers:
|
||
return []
|
||
|
||
approvers_list = []
|
||
|
||
if isinstance(approvers, str):
|
||
# 去除首尾空格
|
||
approvers = approvers.strip()
|
||
|
||
# 如果字符串为空,返回空列表
|
||
if not approvers:
|
||
return []
|
||
|
||
# 尝试解析 JSON 字符串格式(如 "[1,2,3]" 或 "[5]" 或 '"[1,2,3]"')
|
||
try:
|
||
# 先尝试直接解析 JSON
|
||
parsed = json.loads(approvers)
|
||
if isinstance(parsed, list):
|
||
approvers_list = [str(a).strip() for a in parsed if a is not None and str(a).strip()]
|
||
elif isinstance(parsed, str):
|
||
# 如果是字符串,可能是被双重编码的JSON字符串,再解析一次
|
||
# 例如:'"[5]"' -> "[5]" -> [5]
|
||
try:
|
||
parsed2 = json.loads(parsed)
|
||
if isinstance(parsed2, list):
|
||
approvers_list = [str(a).strip() for a in parsed2 if a is not None and str(a).strip()]
|
||
elif isinstance(parsed2, (int, str)):
|
||
# 单个值,转换为列表
|
||
approvers_list = [str(parsed2).strip()]
|
||
except (json.JSONDecodeError, ValueError, TypeError):
|
||
# 双重解析失败,可能是普通字符串,按逗号分隔处理
|
||
approvers_list = [parsed.strip()] if parsed.strip() else []
|
||
elif isinstance(parsed, (int, str)):
|
||
# 单个值(数字或字符串),转换为列表
|
||
approvers_list = [str(parsed).strip()]
|
||
except (json.JSONDecodeError, ValueError, TypeError):
|
||
# JSON 解析失败,尝试按逗号分隔处理
|
||
# 例如:"1,2,3" 或 "5" 或 "张三,李四"
|
||
approvers_list = [a.strip() for a in approvers.split(',') if a.strip()]
|
||
elif isinstance(approvers, list):
|
||
# 数组格式(推荐)
|
||
approvers_list = [str(a).strip() for a in approvers if a is not None and str(a).strip()]
|
||
elif isinstance(approvers, (int, float)):
|
||
# 单个数字,转换为列表
|
||
approvers_list = [str(int(approvers))]
|
||
else:
|
||
return []
|
||
|
||
if not approvers_list:
|
||
return []
|
||
|
||
# 判断是ID还是用户名:如果第一个元素是纯数字,则认为是ID列表
|
||
# 否则认为是用户名列表(兼容旧接口)
|
||
first_item = approvers_list[0]
|
||
is_id_list = str(first_item).strip().isdigit()
|
||
|
||
if is_id_list:
|
||
# ID列表:转换为用户名列表
|
||
try:
|
||
user_ids = [int(uid) for uid in approvers_list]
|
||
users = User.objects.filter(id__in=user_ids, is_deleted=False)
|
||
# 保持原有顺序
|
||
user_dict = {user.id: user.username for user in users}
|
||
username_list = [user_dict.get(uid, None) for uid in user_ids]
|
||
# 过滤掉不存在的用户
|
||
username_list = [name for name in username_list if name is not None]
|
||
return username_list
|
||
except (ValueError, TypeError):
|
||
# 如果转换失败,返回空列表
|
||
return []
|
||
else:
|
||
# 用户名列表(兼容旧接口):直接返回
|
||
return approvers_list
|
||
|
||
|
||
def get_approvers_from_record(business_record, approval=None):
|
||
"""
|
||
从业务记录中获取审核人列表(统一方法)
|
||
|
||
优先从 approvers_order 字段读取(JSON格式),
|
||
如果没有则从 Approval.content 字段解析(兼容旧数据)
|
||
|
||
Args:
|
||
business_record: 业务记录对象(如 Schedule, Reimbursement, Income 等)
|
||
approval: Approval对象(可选,用于从content字段解析审核人列表)
|
||
|
||
Returns:
|
||
list: 审核人列表,如 ["张三", "李四", "王五"],如果没有则返回空列表
|
||
"""
|
||
import json
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 优先从 approvers_order 字段读取
|
||
if hasattr(business_record, 'approvers_order') and business_record.approvers_order:
|
||
try:
|
||
approvers_list = json.loads(business_record.approvers_order)
|
||
if isinstance(approvers_list, list):
|
||
logger.info(f"get_approvers_from_record: 从 business_record.approvers_order 获取审核人列表: {approvers_list}")
|
||
return approvers_list
|
||
except (json.JSONDecodeError, TypeError) as e:
|
||
logger.warning(f"get_approvers_from_record: 解析 approvers_order 失败: {e}")
|
||
|
||
# 如果 business_record 没有 approvers_order 字段,尝试从 Approval.content 字段解析
|
||
if approval and approval.content:
|
||
try:
|
||
# 格式:审批流程:张三 → 李四 → 王五 → 财务(按顺序审批),当前审批人:张三
|
||
if "审批流程:" in approval.content:
|
||
# 提取审批流程部分
|
||
flow_part = approval.content.split("审批流程:")[1].split("(按顺序审批)")[0]
|
||
# 分割审核人(使用 → 分隔)
|
||
approvers_list = [a.strip() for a in flow_part.split("→") if a.strip()]
|
||
# 移除最后的"财务"
|
||
if approvers_list and approvers_list[-1].strip() == "财务":
|
||
approvers_list = approvers_list[:-1]
|
||
if approvers_list:
|
||
logger.info(f"get_approvers_from_record: 从 Approval.content 解析审核人列表: {approvers_list}")
|
||
return approvers_list
|
||
except Exception as e:
|
||
logger.warning(f"get_approvers_from_record: 从 Approval.content 解析失败: {e}")
|
||
|
||
logger.info(f"get_approvers_from_record: 未找到审核人列表")
|
||
return []
|
||
|
||
|
||
def process_approval_flow(approval, business_record, current_approver, state,
|
||
approval_type, final_state_map=None):
|
||
"""
|
||
统一的审核流程处理函数
|
||
|
||
规则:
|
||
- 如果 business_record 有 approvers_order 字段,从该字段读取审核人列表
|
||
- 如果没有审核人列表(个人团队),直接抄送财务
|
||
- 如果有审核人列表(多人团队),按顺序流转
|
||
- 最后一个审核人通过后,抄送财务
|
||
- 财务审核通过后,完成审批
|
||
|
||
Args:
|
||
approval: Approval对象
|
||
business_record: 业务记录对象(如 Schedule, Reimbursement, Income 等)
|
||
current_approver: 当前审核人(从 approval.personincharge 获取)
|
||
state: 审核状态("已通过" 或 "未通过")
|
||
approval_type: 审批类型(如"待办"、"报销申请"等)
|
||
final_state_map: 状态映射字典,格式:{"已通过": "已完成", "未通过": "未通过"}
|
||
|
||
Returns:
|
||
tuple: (是否完成, 错误信息)
|
||
"""
|
||
import json
|
||
from .models import Approval
|
||
|
||
if final_state_map is None:
|
||
final_state_map = {"已通过": "已完成", "未通过": "未通过"}
|
||
|
||
# 如果审核不通过,直接结束
|
||
if state == "未通过":
|
||
approval.state = "未通过"
|
||
approval.save(update_fields=['state'])
|
||
|
||
if business_record and hasattr(business_record, 'state'):
|
||
business_record.state = final_state_map.get("未通过", "未通过")
|
||
business_record.save(update_fields=['state'])
|
||
|
||
return True, None
|
||
|
||
# 检查当前是否已经是财务审核(部门ID或“财务”字符串均视为财务阶段)
|
||
if is_finance_personincharge(approval.personincharge) and approval.state == "已抄送财务":
|
||
# 财务部审核逻辑:财务部只需要一个人审核完即可完成
|
||
if state == "已通过":
|
||
approval.state = "已通过"
|
||
approval.save(update_fields=['state'])
|
||
|
||
if business_record and hasattr(business_record, 'state'):
|
||
business_record.state = final_state_map.get("已通过", "已完成")
|
||
business_record.save(update_fields=['state'])
|
||
|
||
return True, None
|
||
|
||
# 获取审核人列表
|
||
approvers_list = get_approvers_from_record(business_record, approval=approval)
|
||
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.info(f"process_approval_flow: 审批类型={approval_type}, 当前审核人={current_approver}, 审核状态={state}, 审核人列表={approvers_list}")
|
||
|
||
if not approvers_list:
|
||
# 没有审核人列表(个人团队或直接到财务)
|
||
# 如果当前不是财务,则抄送财务
|
||
if not is_finance_personincharge(approval.personincharge):
|
||
finance_personincharge = get_finance_personincharge_value()
|
||
approval.personincharge = finance_personincharge
|
||
approval.state = "已抄送财务"
|
||
if "已抄送财务" not in approval.content and "已抄送财务部" not in approval.content:
|
||
approval.content = approval.content + ",已抄送财务部"
|
||
approval.save(update_fields=['state', 'personincharge', 'content'])
|
||
|
||
if business_record and hasattr(business_record, 'state'):
|
||
business_record.state = "待财务处理"
|
||
business_record.save(update_fields=['state'])
|
||
return False, None
|
||
|
||
# 有审核人列表(多人团队),按顺序流转
|
||
try:
|
||
current_index = approvers_list.index(current_approver)
|
||
logger.info(f"process_approval_flow: 当前审核人在列表中的位置: {current_index}/{len(approvers_list)-1}")
|
||
except ValueError:
|
||
# 当前审核人不在列表中,可能是旧数据,直接抄送财务
|
||
logger.warning(f"process_approval_flow: 当前审核人 {current_approver} 不在审核人列表中 {approvers_list},直接抄送财务")
|
||
approval.personincharge = "财务"
|
||
approval.state = "已抄送财务"
|
||
approval.save(update_fields=['state', 'personincharge'])
|
||
return False, None
|
||
|
||
# 检查是否还有下一个审核人
|
||
if current_index < len(approvers_list) - 1:
|
||
# 不是最后一个审核人,流转到下一个
|
||
next_approver = approvers_list[current_index + 1]
|
||
logger.info(f"process_approval_flow: 流转到下一个审核人: {next_approver} (位置: {current_index + 1})")
|
||
approval.personincharge = next_approver
|
||
approval.state = "审核中"
|
||
# 更新审批内容,显示当前审批人
|
||
if "当前审批人:" in approval.content:
|
||
approval.content = approval.content.replace(
|
||
f"当前审批人:{current_approver}",
|
||
f"当前审批人:{next_approver}"
|
||
)
|
||
approval.save(update_fields=['state', 'personincharge', 'content'])
|
||
logger.info(f"process_approval_flow: 已更新审批记录,personincharge={next_approver}, state=审核中")
|
||
return False, None
|
||
else:
|
||
# 最后一个审核人,抄送财务
|
||
logger.info(f"process_approval_flow: 最后一个审核人已审核,流转到财务部")
|
||
finance_personincharge = get_finance_personincharge_value()
|
||
approval.personincharge = finance_personincharge
|
||
approval.state = "已抄送财务"
|
||
if "已抄送财务" not in approval.content and "已抄送财务部" not in approval.content:
|
||
approval.content = approval.content + ",已抄送财务部"
|
||
approval.save(update_fields=['state', 'personincharge', 'content'])
|
||
|
||
if business_record and hasattr(business_record, 'state'):
|
||
business_record.state = "待财务处理"
|
||
business_record.save(update_fields=['state'])
|
||
|
||
logger.info(f"process_approval_flow: 已抄送财务部,personincharge=%s, state=已抄送财务", approval.personincharge)
|
||
return False, None
|
||
|
||
|
||
def create_approval_with_team_logic(team_name, approvers, title, content, approval_type, user_id,
|
||
business_record=None, today=None):
|
||
"""
|
||
根据团队类型创建审批记录(统一逻辑)
|
||
|
||
规则:
|
||
- 个人团队(personal):直接抄送财务(personincharge="财务",state="已抄送财务")
|
||
- 团队(team):需要审核人,按顺序审核,最后抄送财务
|
||
- 无团队:直接抄送财务
|
||
|
||
Args:
|
||
team_name: 团队名称
|
||
approvers: 审核人列表(可以是数组或字符串,多人团队时需要)
|
||
- 推荐格式:用户ID列表,如 [1, 2, 3] 或 ["1", "2", "3"]
|
||
- 兼容格式:用户名列表,如 ["张三", "李四", "王五"]
|
||
- 字符串格式:逗号分隔的ID或用户名,如 "1,2,3" 或 "张三,李四,王五"
|
||
title: 审批标题
|
||
content: 审批内容
|
||
approval_type: 审批类型(如"立项登记"、"付款申请"等)
|
||
user_id: 关联的业务ID(字符串)
|
||
business_record: 业务记录对象(可选,用于更新状态和存储approvers_order)
|
||
today: 日期字符串(可选,格式:YYYY-MM-DD)
|
||
|
||
Returns:
|
||
tuple: (approval对象, approvers_order_json, 是否需要审核)
|
||
"""
|
||
import json
|
||
from datetime import datetime
|
||
from .models import Team, Approval, User
|
||
|
||
if today is None:
|
||
today = datetime.now().strftime("%Y-%m-%d")
|
||
|
||
# 查找团队
|
||
team = None
|
||
if team_name:
|
||
try:
|
||
team = Team.objects.prefetch_related('approvers').filter(name=team_name, is_deleted=False).first()
|
||
except:
|
||
team = None
|
||
|
||
# 优先检查是否传入了审核人(无论团队类型如何,如果传入了审核人,都应该使用)
|
||
approvers_list = parse_approvers(approvers)
|
||
|
||
# 如果传入了审核人,使用传入的审核人(即使团队类型是personal或无团队)
|
||
if approvers_list:
|
||
# 验证审核人是否存在,过滤掉无效的审核人
|
||
valid_approvers_list = []
|
||
invalid_approvers_list = []
|
||
for approver_name in approvers_list:
|
||
if User.objects.filter(username=approver_name, is_deleted=False).exists():
|
||
valid_approvers_list.append(approver_name)
|
||
else:
|
||
invalid_approvers_list.append(approver_name)
|
||
|
||
# 如果所有审核人都无效,返回 None
|
||
if not valid_approvers_list:
|
||
return None, None, True # 审核人不存在
|
||
|
||
# 使用有效的审核人列表
|
||
approvers_list = valid_approvers_list
|
||
|
||
# 将审核人顺序存储为JSON格式
|
||
approvers_order_json = json.dumps(approvers_list, ensure_ascii=False)
|
||
|
||
# 存储到业务记录
|
||
if business_record and hasattr(business_record, 'approvers_order'):
|
||
business_record.approvers_order = approvers_order_json
|
||
business_record.state = "审核中"
|
||
business_record.save(update_fields=['approvers_order', 'state'])
|
||
|
||
# 创建审批记录,第一个审核人
|
||
first_approver = approvers_list[0]
|
||
approvers_str = ' → '.join(approvers_list) # 使用箭头表示顺序
|
||
content_with_flow = f"{content},审批流程:{approvers_str} → 财务部(按顺序审批),当前审批人:{first_approver}"
|
||
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.info(f"create_approval_with_team_logic: 创建审批记录(使用传入的审核人)- 审批类型={approval_type}, 审核人列表={approvers_list}, 第一个审核人={first_approver}, approvers_order_json={approvers_order_json}")
|
||
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content_with_flow,
|
||
times=today,
|
||
personincharge=first_approver,
|
||
state="审核中",
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
|
||
logger.info(f"create_approval_with_team_logic: 审批记录已创建 - ID={approval.id}, personincharge={approval.personincharge}, state={approval.state}")
|
||
|
||
return approval, approvers_order_json, True
|
||
|
||
# 如果没有传入审核人,则根据团队类型判断
|
||
# 判断团队类型
|
||
if not team_name or not team or (team and team.team_type == 'personal'):
|
||
# 个人团队或无团队:直接到财务团队审核
|
||
finance_personincharge = get_finance_personincharge_value()
|
||
content_to_save = content
|
||
if "已抄送财务" not in content and "已抄送财务部" not in content:
|
||
content_to_save = content + ",已抄送财务部"
|
||
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content_to_save,
|
||
times=today,
|
||
personincharge=finance_personincharge,
|
||
state="已抄送财务",
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
|
||
# 更新业务记录状态
|
||
if business_record:
|
||
business_record.state = "待财务处理"
|
||
business_record.save(update_fields=['state'])
|
||
|
||
return approval, None, False
|
||
|
||
elif team and team.team_type == 'team':
|
||
# 团队类型:需要审核人审核(按顺序)
|
||
# 如果没有传入审核人,使用团队的审核人
|
||
team_approvers = team.approvers.filter(is_deleted=False).order_by('id')
|
||
approvers_list = [approver.username for approver in team_approvers]
|
||
|
||
if not approvers_list:
|
||
return None, None, True # 需要审核但没有审核人
|
||
|
||
# 验证审核人是否存在,过滤掉无效的审核人
|
||
valid_approvers_list = []
|
||
invalid_approvers_list = []
|
||
for approver_name in approvers_list:
|
||
if User.objects.filter(username=approver_name, is_deleted=False).exists():
|
||
valid_approvers_list.append(approver_name)
|
||
else:
|
||
invalid_approvers_list.append(approver_name)
|
||
|
||
# 如果所有审核人都无效,返回 None
|
||
if not valid_approvers_list:
|
||
return None, None, True # 审核人不存在
|
||
|
||
# 使用有效的审核人列表
|
||
approvers_list = valid_approvers_list
|
||
|
||
# 将审核人顺序存储为JSON格式
|
||
approvers_order_json = json.dumps(approvers_list, ensure_ascii=False)
|
||
|
||
# 存储到业务记录
|
||
if business_record and hasattr(business_record, 'approvers_order'):
|
||
business_record.approvers_order = approvers_order_json
|
||
business_record.state = "审核中"
|
||
business_record.save(update_fields=['approvers_order', 'state'])
|
||
|
||
# 创建审批记录,第一个审核人
|
||
first_approver = approvers_list[0]
|
||
approvers_str = ' → '.join(approvers_list) # 使用箭头表示顺序
|
||
content_with_flow = f"{content},审批流程:{approvers_str} → 财务部(按顺序审批),当前审批人:{first_approver}"
|
||
|
||
import logging
|
||
logger = logging.getLogger(__name__)
|
||
logger.info(f"create_approval_with_team_logic: 创建审批记录(使用团队配置的审核人)- 审批类型={approval_type}, 审核人列表={approvers_list}, 第一个审核人={first_approver}, approvers_order_json={approvers_order_json}")
|
||
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content_with_flow,
|
||
times=today,
|
||
personincharge=first_approver,
|
||
state="审核中",
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
|
||
logger.info(f"create_approval_with_team_logic: 审批记录已创建 - ID={approval.id}, personincharge={approval.personincharge}, state={approval.state}")
|
||
|
||
return approval, approvers_order_json, True
|
||
|
||
else:
|
||
# 找不到团队或团队类型未知,直接抄送财务
|
||
finance_personincharge = get_finance_personincharge_value()
|
||
content_to_save = content
|
||
if "已抄送财务" not in content and "已抄送财务部" not in content:
|
||
content_to_save = content + ",已抄送财务部"
|
||
|
||
approval = Approval.objects.create(
|
||
title=title,
|
||
content=content_to_save,
|
||
times=today,
|
||
personincharge=finance_personincharge,
|
||
state="已抄送财务",
|
||
type=approval_type,
|
||
user_id=str(user_id)
|
||
)
|
||
|
||
# 更新业务记录状态
|
||
if business_record:
|
||
business_record.state = "待财务处理"
|
||
business_record.save(update_fields=['state'])
|
||
|
||
return approval, None, False |