Files
gzth/soong_workflow/wizard/soong_workflow_comment_wizard.py
T
2026-09-14 16:04:52 +08:00

1474 lines
88 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from email.policy import default
from odoo import fields, models, api
from odoo.exceptions import UserError, ValidationError
from datetime import datetime, date, time, timedelta
import logging
_logger = logging.getLogger(__name__)
class SoongWorkflowCommentWizard(models.TransientModel):
_name = "soong.workflow.comment.wizard"
_description = "Soong Workflow Comment Wizard"
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin']
_transient_max_count = 0
name = fields.Char(string="name")
res_model = fields.Char()
res_id = fields.Integer()
code = fields.Char()
log_ids = fields.Many2many(comodel_name="soong.workflow.log")
node_btn_id = fields.Many2one(comodel_name="soong.workflow.node.button")
node_btn_name = fields.Char(string="动作名称", related='node_btn_id.name')
user_id = fields.Many2one('res.users', string='用户', default=lambda self: self.env.user)
user_role_ids = fields.Many2many('soong.user.role', string="角色", compute='_compute_user_role_ids', store=True)
node_link_len = fields.Char(string="转交2")
is_supervising = fields.Boolean(string="是否督办")
comment_note_id = fields.Many2one('soong.note', string="常用意见")
comment_note = fields.Text(string="意见")
illustrate = fields.Text(string="说明")
comment = fields.Char(compute='_compute_comment', store=True)
state_list = fields.Char(string="状态列表")
record_id = fields.Integer()
workflow_record_id = fields.Many2one('soong.workflow.record', string="流程")
is_selection = fields.Boolean(string="是否自动选择")
trans_link_id = fields.Many2one('trans.link', string='转交节点', ondelete='set null')
document_ids = fields.Many2many('ir.attachment', string="附件")
return_link_id = fields.Many2one('trans.link', string='退回节点', ondelete='set null')
invalid_link_ids = fields.Many2many(
'trans.link',
string='跳过相同人员',
help='跳过相同人员的节点'
)
employee_trans_domain_ids = fields.Many2many('hr.employee', 'employee_trans_domain_ids_rel', string="传阅人员", store=True)
employee_ids = fields.Many2many('hr.employee', 'employee_ids_rel', 'custom_id', 'employee_id', string="转交人员", store=True)
circulated_employee_ids = fields.Many2many('hr.employee', 'circulated_employee_rel', 'custom_id', 'employee_id', help='只可以看不可以评论,看过以后状态自动变为已阅', string="传阅人员", store=True)
dep_manager_employee_ids = fields.Many2many('hr.employee', 'dep_manager_employee_rel', 'custom_id', 'employee_id', string="承办", store=True)
add_employee_ids = fields.Many2many('hr.employee', 'add_employee_rel', 'custom_id', 'employee_id', string="加签人员", help='点击确认以后评论的人员', store=True)
add_employee_filter_ids = fields.Many2many('hr.employee', 'add_employee_filter_rel', 'custom_id', 'employee_id', string="加签过滤人员",
default=lambda self: self._default_add_employee_filter_ids(),
help='当前登录人所在部门及子部门(递归)的员工,用于过滤加签人员可选范围')
is_return = fields.Boolean('是否返回', help='勾选之后,加签返回自己')
supervising_employee_ids = fields.Many2many('hr.employee', 'supervising_employee_rel', 'custom_id', 'employee_id', string="督办人员", store=True)
approval_concatenation = fields.Selection([('chuan', '串申'), ('bing', '并申')], string="审批形式",
help='并:任何人均给意见,最后一个完成的人转交到下一节点.'
'串:都会收到消息,但任意一个人转交后,即到下一节点,其他人将不再显示,相当于收回.')
is_automatic = fields.Boolean('是否自动匹配')
company_id = fields.Many2one('res.company', '公司', default=lambda self: self.env.user.company_id)
scope_type = fields.Selection(
[
('company', '本公司'),
('group', '本集团'),
('department', '本部门'),
],
string='范围类型',
default='company',
required=True,
)
employee_ids1 = fields.Many2many('hr.employee', 'employee_ids1_rel', 'custom_id1', 'employee_id1', string="人员范围", store=True)
employee_ids2 = fields.Many2many('hr.employee', 'employee_ids2_rel', 'custom_id2', 'employee_id2', string="加签传阅人员过滤")
@api.onchange('is_selection', 'scope_type')
def _onchange_is_selection(self):
if not self.employee_ids1:
self.employee_ids = False
return
domain = [('id', 'in', self.employee_ids1.ids)]
if self.scope_type == 'company':
domain.append(('company_id', '=', self.company_id.id))
elif self.scope_type == 'group':
pass
elif self.scope_type == 'department':
department = self.env.user.department_id
if department:
all_depts = department.child_ids | department
domain.extend([
('company_id', '=', self.company_id.id),
'|',
('department_id', 'in', all_depts.ids),
('department_ids', 'in', all_depts.ids),
])
all_employees = self.env['hr.employee'].search(domain)
self.employee_ids = all_employees if self.is_selection else False
@api.onchange('scope_type', 'department_id', 'company_id', 'employee_ids1')
def _onchange_employee_ids1(self):
if self.is_selection:
return
if not self.employee_ids1:
self.employee_ids = False
return
filtered_employees = self.employee_ids1
# 范围过滤
if self.scope_type == 'company':
filtered_employees = self.employee_ids1.filtered(
lambda e: e.company_id == self.company_id
)
elif self.scope_type == 'department':
department = self.env.user.department_id
if department:
all_depts = department.child_ids | department
filtered_employees = self.employee_ids1.filtered(
lambda e: e.department_id in all_depts or any(d in all_depts for d in e.department_ids)
)
if len(filtered_employees) == 1:
self.employee_ids = filtered_employees
@api.onchange('scope_type', 'department_id', 'company_id')
def _onchange_employee_ids2(self):
domain = []
if self.scope_type == 'company':
domain.append(('company_id', '=', self.company_id.id))
elif self.scope_type == 'department':
department = self.env.user.department_id
if department:
all_depts = department.child_ids | department
domain.extend([
('company_id', '=', self.company_id.id),
'|',
('department_id', 'in', all_depts.ids),
('department_ids', 'in', all_depts.ids),
])
self.employee_ids2 = self.env['hr.employee'].search(domain)
def _default_add_employee_filter_ids(self):
"""获取当前登录人所在部门及子部门(递归)的员工,作为加签人员的可选范围。
参考 yuthon_notice._constrains_employee_related_fields 中的
get_department_employees(department_id),递归获取部门及其所有子部门下的员工。
"""
department = self.env.user.department_id
if not department:
return self.env['hr.employee']
result = self.env['hr.employee']
def collect(dept):
nonlocal result
# 当前部门(含多部门字段 department_ids)下的员工
emps = self.env['hr.employee'].search([
('company_id', '=', self.env.user.company_id.id),
'|',
('department_id', '=', dept.id),
('department_ids', 'in', dept.ids),
])
result |= emps
# 递归处理子部门(部门及以下)
for child in dept.child_ids:
collect(child)
collect(department)
return result
@api.depends('comment_note_id', 'comment_note')
def _compute_comment(self):
for record in self:
comment_parts = []
if record.comment_note_id:
comment_parts.append(record.comment_note_id.name)
if record.comment_note:
comment_parts.append(record.comment_note)
record.comment = " ".join(comment_parts) if comment_parts else ""
@api.depends('user_id.employee_ids.user_role_ids', 'user_id.employee_ids.user_role_id')
def _compute_user_role_ids(self):
for record in self:
if record.user_id and record.user_id.employee_ids:
role_ids_from_employee = record.user_id.employee_ids.user_role_ids
role_id_from_field = record.user_id.employee_ids.user_role_id
if role_id_from_field:
record.user_role_ids = role_ids_from_employee | role_id_from_field
else:
record.user_role_ids = role_ids_from_employee
else:
record.user_role_ids = False
@api.onchange('record_id')
def _onchange_record_id(self):
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
self.record_id = record_id
@api.onchange('return_link_id')
def onchange_employee_ids(self):
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
for ret in self.return_link_id:
if ret.name in ['拟办', '申请人']:
if hasattr(record_id, 'user_id') and record_id.user_id:
employee_ids1 = self.env['hr.employee'].search([('user_id', '=', record_id.user_id.id)])
elif hasattr(record_id, 'users_id') and record_id.users_id:
employee_ids1 = self.env['hr.employee'].search([('user_id', '=', record_id.users_id.id)])
self.employee_ids1 = employee_ids1
elif self.node_btn_name == '退回':
target_log = record_id.log_ids.filtered(lambda l: l.name == ret.name)
if target_log and target_log.done_by:
if target_log.done_by:
employee = self.env['hr.employee'].sudo().search([('user_id', '=', target_log.done_by.id)], limit=1)
self.employee_ids1 = employee
else:
self.employee_ids1 = self.env['hr.employee']
# next_list = []
# workflow_id = self.return_link_id.node_button_id.workflow_id
# state_list = [i.node_name for i in workflow_id.node_ids]
# if ret.name == '询价':
# name = 'draft'
# else:
# name = ret.name
# self_state = state_list.index(name)
# button_ids = workflow_id.node_ids[self_state].button_ids
# for btn in button_ids:
# next_list.append(btn.name)
# if '转交' not in next_list:
# raise UserError('配置错误,请配置当前{}的转交按钮'.format(btn.name))
# else:
# if btn.is_automatic:
# employee_user_role_id = self.env['hr.employee'].search(
# ['|', ('user_role_id', '=', btn.users_role_id.id),
# ('user_role_id', 'in', btn.users_role_group_ids.user_role_ids.ids),
# ('department_id', '=', record_id.department_id.id)])
# self.employee_ids1 = employee_user_role_id
# else:
# employee_user_role_ids = self.env['hr.employee'].search(
# ['|', ('user_role_id', '=', btn.users_role_id.id),
# ('user_role_id', 'in', btn.users_role_group_ids.user_role_ids.ids),
# ])
# self.employee_ids1 = employee_user_role_ids
def _get_entrust_substitution(self, model_name):
"""返回 {委托人 user_id: 被委托人 hr.employee} 映射。
仅包含对当前模型(或在"全部流程"中)生效且在有效期内的流程委托(soong.workflow.entrust)。
"""
today = fields.Date.today()
substitution = {}
entrust_all = self.env['ir.model'].sudo().search([('model', '=', 'entrust.all')], limit=1)
model = self.env['ir.model'].sudo().search([('model', '=', model_name)], limit=1)
if not model and not entrust_all:
return substitution
entrusts = self.env['soong.workflow.entrust'].sudo().search([
('is_enable', '=', True),
('start_date', '<=', today),
'|',
('end_date', '>', today),
('end_date', '=', False)
])
for ent in entrusts:
model_ids = ent.model_ids.ids
# 命中"全部流程"(entrust.all) 或 当前业务模型
in_scope = (entrust_all and entrust_all.id in model_ids) or (model and model.id in model_ids)
if in_scope and ent.entrust_user_id and ent.entrust_user_id2:
delegatee = self.env['hr.employee'].sudo().search(
[('user_id', '=', ent.entrust_user_id2.id)], limit=1)
if delegatee:
substitution[ent.entrust_user_id.id] = delegatee
return substitution
@api.onchange('trans_link_id')
def onchange_trans_employee_ids(self):
for record in self:
# 先清空
record_id = self.env[record.res_model].browse(record.res_id) if record.res_model and record.res_id else None
ctx_links = self.env.context.get('all_workflow_links', [])
valid_links = self.env['trans.link'].browse(ctx_links) if isinstance(ctx_links, (list, tuple)) else ctx_links
record.is_selection = False
if self.node_btn_name == '退回':
record.scope_type = 'group'
else:
record.scope_type = 'company'
if record.node_btn_id.employee_ids and record.node_btn_id.is_carbon_copy:
record.circulated_employee_ids = record.node_btn_id.employee_ids
if not record.trans_link_id:
continue
# 排除规则:流程退回 不限制
if record_id and record_id.workflow_id:
has_exclude_log = record_id.log_ids.filtered(lambda l: l.node_btn_name == '1')
if not has_exclude_log:
# 1. 从 valid_links 里 匹配出 当前流程会经过的【必经节点】
current_mandatory_nodes = self.env['soong.workflow.node']
for link in valid_links:
link_node = record_id.workflow_id.node_ids.search([
('name', '=', link.name),
('workflow_id', '=', record_id.workflow_id.id)
], limit=1)
if link_node and link_node.is_mandatory_node:
current_mandatory_nodes |= link_node
if current_mandatory_nodes:
# 2. 取第一个必经节点
first_mandatory = current_mandatory_nodes.sorted('sequence')[0]
# 3. 只允许选择 <= 第一个必经节点 的链接
allow_links = self.env['trans.link']
for link in valid_links:
link_node = record_id.workflow_id.node_ids.search([
('name', '=', link.name),
('workflow_id', '=', record_id.workflow_id.id)
], limit=1)
if link_node and link_node.sequence <= first_mandatory.sequence:
allow_links |= link
# 4. 校验:选了必经节点之后的 → 报错
if record.trans_link_id not in allow_links:
record.trans_link_id = False
raise UserError(f"节点【{first_mandatory.name}】是必经节点,不可跳过!")
for link in record.trans_link_id:
employee_user_role_ids = self.env['hr.employee']
if link.name == '印章管理员':
if hasattr(record_id, 'type_stamp_ids'):
stamp_types = record_id.type_stamp_ids
if stamp_types:
user_ids = self.env['res.users']
for stamp in stamp_types:
user_ids |= stamp.user_ids
if user_ids:
employee_user_role_ids = self.env['hr.employee'].sudo().search([
('user_id', 'in', user_ids.ids)
])
else:
employee_user_role_ids = record_id._get_approvers_for_link(link, record_id)
workflow_id = link.node_button_id.workflow_id
node = workflow_id.node_ids.search([
('name', '=', link.name),
('workflow_id', '=', workflow_id.id)
], limit=1)
if node:
record.approval_concatenation = node.approval_concatenation
record.employee_trans_domain_ids = employee_user_role_ids
record.employee_ids1 = employee_user_role_ids
workflow_id = link.node_button_id.workflow_id
node = workflow_id.node_ids.search([
('name', '=', link.name),
('workflow_id', '=', workflow_id.id)
])
button_priority_list = ["分发", "下发", "转交", "结束"]
button = False
if node.button_ids:
for btn_name in button_priority_list:
for btn in node.button_ids:
if btn.name == btn_name: # 匹配优先级按钮名称
is_in_scope = True
if not btn.is_review_invisible:
button = btn
break
if btn.is_review_invisible:
domain = eval(btn.invisible_expression)
model = record_id.env[record_id._name]
matched = model.search(domain + [('id', '=', record_id.id)])
is_in_scope = bool(matched)
if is_in_scope:
target_btn = btn
button = target_btn
break
if button:
if button.is_node_automatic:
record.is_selection = True
else:
record.is_selection = False
record._onchange_is_selection()
def _send_workflow_notifications(self, partners=None):
""" 通知前端刷新红点、播放音频
:param partners: 需要通知的用户(res.partner),未传则通知所有
"""
bus_message = {'type': 'wkf_notify'}
notifications = [[partner, 'sun_badge_menu.reload_menu_data', [bus_message]] for partner in partners]
self.env["bus.bus"]._sendmany(notifications) # 浏览器叮咚
def add_comment(self):
self.ensure_one()
partners = self._get_all_employee_partners()
self._send_workflow_notifications(partners)
# 如果选择了常用意见,将使用信息追加到 soong.note 记录
if self.comment_note_id:
import pytz
from datetime import datetime
tz = pytz.timezone('Asia/Shanghai')
today = datetime.now(pytz.utc).astimezone(tz).strftime('%Y-%m-%d')
user_name = self.env.user.name
code = self.code or ''
note_name = self.comment_note_id.name or ''
new_line = f"{today} {code} {user_name} {note_name}"
old_info = self.comment_note_id.use_info or ''
updated_info = (old_info + '\n' + new_line) if old_info else new_line
self.comment_note_id.sudo().write({'use_info': updated_info})
cx = self.env.context.copy() or {}
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
is_admin = self.env.user.has_group('soong_workflow.group_soong_workflow_manager')
log_done_by_ids = [log.done_by.id for log in record_id.log_ids if log.done_by]
if self.node_btn_name == '加签':
current_node_log = self.sudo().env["soong.workflow.log"].search(
[
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("done_by", "=", self.env.user.id),
("approval_state", 'in', ['0','1']),
],
order="create_date DESC",
limit=1
)
current_node_log.write({
"comment": self.comment,
"approval_state": "2", # 已完成
'document_ids': [(6, 0, self.document_ids.ids)],
"lase_click_date": fields.Datetime.now()
})
if hasattr(record_id, 'user_id') and record_id.user_id:
query_user_id = record_id.user_id
elif hasattr(record_id, 'users_id') and record_id.users_id:
query_user_id = record_id.users_id
description = f"标题:{record_id.name}\n申请人:{query_user_id.name}"
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
for employee_id in self.add_employee_ids:
next_node_log_records = self.sudo().env["soong.workflow.log"].create({
'name': '加签',
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee_id.user_id.id,
"add_sign_by": self.env.user.id,
"is_return" : self.is_return,
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form'
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee_id.wecom_userid,
title=record_id._description + "加签",
description=description, url=url,
btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
target_logs = next_node_log_records.filtered(lambda l: l.done_by.id == employee_id.user_id.id)
if target_logs:
target_logs.msg_id = msg_id
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
workflow_record_id.employee_ids = self.add_employee_ids
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.add_employee_ids if
employee.user_id]
if self.node_btn_name == '转交' or (self.node_btn_name == '加签' and not self.is_return):
if self.res_model == 'issue.approval':
if self.trans_link_id.name != '申请人':
if record_id.issue_approval_ids:
for issue_library in record_id.issue_approval_ids:
issue_library.issue_library_id.issue_state = 'to_draft'
has_comment_note = bool(self.comment_note_id)
has_comment = bool(self.comment and self.comment.strip())
if not has_comment_note and not has_comment:
raise UserError('请填写备注')
if not self.employee_ids and self.trans_link_id:
raise UserError("必须有转交人员")
self_node_id = record_id.workflow_id.node_ids.search(
[('name', '=', record_id.state), ('workflow_id', '=', record_id.workflow_id.id)])
log = self.env['soong.workflow.log'].search([("model", "=", self.res_model),
("res_id", "=", self.res_id)])
query_user_id = self.env.user.id
if is_admin and record_id:
if hasattr(record_id, 'user_id') and record_id.user_id:
query_user_id = record_id.user_id.id
elif hasattr(record_id, 'users_id') and record_id.users_id:
query_user_id = record_id.users_id.id
if len(log) > 1 and is_admin:
current_node_log = self.sudo().env["soong.workflow.log"].search(
[("model", "=", self.res_model),
("res_id", "=", self.res_id),
("node_id", "=", self.node_btn_id.node_id.id)
],
order="lase_click_date DESC",
limit=1
)
else:
current_node_log = self.sudo().env["soong.workflow.log"].search(
[
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("done_by", "=", query_user_id),
("node_id", "=", self.node_btn_id.node_id.id),
],
order="create_date DESC",
limit=1
)
# 创建或更新当前用户的审批记录
if not current_node_log:
self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": query_user_id,
"node_btn_name": "0",
"node_btn_id": self.node_btn_id.id,
"approval_state": "2", # 已完成
"lase_click_date": fields.Datetime.now(),
"comment": self.comment,
})
else:
current_node_log.write({
"comment": self.comment,
"approval_state": "2", # 已完成
'document_ids': [(6, 0, self.document_ids.ids)],
"lase_click_date": fields.Datetime.now()
})
if current_node_log.node_btn_name == '3':
other_node_logs = self.sudo().env["soong.workflow.log"].search(
[("model", "=", self.res_model),
("res_id", "=", self.res_id),
("id", "!=", current_node_log.id),
("node_id", "=", self.node_btn_id.node_id.id)
])
for other_node_log in other_node_logs:
if getattr(other_node_log, 'msg_id', False):
self.sudo().env["wecom.apps"].back_message(other_node_log.msg_id, category='approval')
other_node_log.unlink()
if getattr(current_node_log, 'stamp_id', False):
same_stamp_logs = self.sudo().env["soong.workflow.log"].search(
[("model", "=", self.res_model),
("res_id", "=", self.res_id),
("id", "!=", current_node_log.id),
("node_id", "=", self.node_btn_id.node_id.id),
("stamp_id", "=", current_node_log.stamp_id.id),
("approval_state", "in", ['0', '1']),
])
for log_delete in same_stamp_logs:
if getattr(log_delete, 'msg_id', False):
self.sudo().env["wecom.apps"].back_message(log_delete.msg_id, category='approval')
same_stamp_logs.unlink()
# 印章分组审批:模型有 type_stamp_ids 字段且转交目标是「印章管理员」节点
record_has_stamps = hasattr(record_id, 'type_stamp_ids') and bool(record_id.type_stamp_ids)
is_transfer_to_stamp = self.trans_link_id and self.trans_link_id.name == '印章管理员'
is_stamp_reviewer = bool(current_node_log and getattr(current_node_log, 'stamp_id', False))
# 流程委托:当前模型(或"全部流程")且在有效期内的 {委托人 user_id: 被委托人 hr.employee}
entrust_substitution = self._get_entrust_substitution(self.res_model) if self.node_btn_name == '转交' else {}
if is_transfer_to_stamp and record_has_stamps:
# 场景1:转交到印章管理员节点,按章分组建待办(章间并审、章内串审)
next_node_id = record_id.workflow_id.node_ids.search([
('name', '=', self.trans_link_id.name),
('workflow_id', '=', record_id.workflow_id.id)
], limit=1)
# 同一人可能兼任多枚章,先按 user 去重(只保留第一枚章),避免重复待办
user_stamp_map = {}
for stamp in record_id.type_stamp_ids:
for user in stamp.user_ids:
if user.id not in user_stamp_map:
user_stamp_map[user.id] = stamp
all_user_ids = []
all_employee_ids = []
for user, stamp in user_stamp_map.items():
employee = self.env['hr.employee'].sudo().search([('user_id', '=', user)], limit=1)
if employee and employee.id not in all_employee_ids:
all_employee_ids.append(employee.id)
all_user_ids.append(user)
self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": user,
"node_btn_name": "4",
"stamp_id": stamp.id,
"approval_state": "0",
})
related_record = self.env[self.res_model].browse(self.res_id)
if hasattr(related_record, 'self_users_ids'):
related_record.self_users_ids = [(6, 0, all_user_ids)] if all_user_ids else [(5,)]
record_id.write({'state': self.trans_link_id.name})
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)])
workflow_record_id.users_ids = [(4, uid) for uid in all_user_ids]
workflow_record_id.employee_ids = all_employee_ids
workflow_record_id.approve_state = "pending"
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
for uid in all_user_ids:
employee = self.env['hr.employee'].sudo().search([('user_id', '=', uid)], limit=1)
if not employee:
continue
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form'
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=record_id._description + "印章审批",
description=description, url=url,
btntxt="查看详情")
# 检查是否为并审模式(印章节点的人提交时也走并审,实现章间并审)
elif is_stamp_reviewer or self.approval_concatenation == 'bing': # 并审模式
expected_employees = self.employee_ids
# 流程委托:把选中的委托人解析为实际审批人(被委托人)
delegation_targets = []
for selected_employee in expected_employees:
actual_employee = selected_employee
is_delegated = False
if selected_employee.user_id and selected_employee.user_id.id in entrust_substitution:
actual_employee = entrust_substitution[selected_employee.user_id.id]
is_delegated = True
delegation_targets.append((selected_employee, actual_employee, is_delegated))
effective_employees = self.env['hr.employee']
for selected_employee, actual_employee, is_delegated in delegation_targets:
if actual_employee:
effective_employees |= actual_employee
if record_id.log_ids2:
log_employee_ids = record_id.log_ids2.mapped('done_by.employee_id.id')
effective_employees = effective_employees.filtered(lambda emp: emp.id not in log_employee_ids)
# 查找下一个节点
if self.trans_link_id:
next_node_id = record_id.workflow_id.node_ids.search([
('name', '=', self.trans_link_id.name),
('workflow_id', '=', record_id.workflow_id.id)
])
completed_logs = self.sudo().env["soong.workflow.log"].search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("node_id", "=", self.node_btn_id.node_id.id),
("node_btn_name", "=", "4"),
("approval_state", "!=", "2")
])
all_completed = len(completed_logs) == 0
next_node_log_records = self.env["soong.workflow.log"]
if all_completed:
for employee in effective_employees:
is_delegated = any(employee.id == actual_employee.id
for selected_employee, actual_employee, is_delegated in delegation_targets
if is_delegated)
# 检查该员工在下一节点是否已有记录
next_node_employee_log = self.sudo().env["soong.workflow.log"].search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("node_id", "=", next_node_id.id),
('node_btn_name', '!=', '1'),
('approval_state', '!=', '2'),
("done_by", "=", employee.user_id.id)
])
if not next_node_employee_log:
# 创建初步记录,状态为"待审批"
next_node_employee_log = self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee.user_id.id,
"done_by_display": f"{employee.name}(委托人)" if is_delegated else False,
"node_btn_name": "4",
"approval_state": "0", # 待审批
})
next_node_log_records += next_node_employee_log
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
# 被委托人(实际经办) + 委托人(原选中人) 都可见
visible_uids = [emp.user_id.id for emp in effective_employees if emp.user_id]
for selected_employee, actual_employee, is_delegated in delegation_targets:
if is_delegated and selected_employee.user_id and selected_employee.user_id.id not in visible_uids:
visible_uids.append(selected_employee.user_id.id)
workflow_record_id.users_ids = [(4, uid) for uid in visible_uids]
workflow_record_id.employee_ids = effective_employees.ids
workflow_record_id.approve_state = "pending" # 待审批
related_record = self.env[self.res_model].browse(self.res_id)
if hasattr(related_record, 'self_users_ids'):
user_ids = [emp.user_id.id for emp in effective_employees if emp.user_id]
related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)]
if not self.trans_link_id.name:
raise UserError('无转交节点')
record_id.write({'state': self.trans_link_id.name})
for employee in effective_employees:
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
_logger.info(f"===================当前的url为{url}=============================")
title = record_id._description
if 'Purchase Order' == record_id._description:
title = '采购订单'
if 'Room Booking' == record_id._description:
title = '会议通知'
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=employee.wecom_userid, title=title,
description="标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(
fields.Datetime.now() + timedelta(hours=8)),
url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
target_logs = next_node_log_records.filtered(lambda l: l.done_by.id == employee.user_id.id)
if target_logs:
target_logs.msg_id = msg_id
else: # 串审模式 (沿用原有逻辑)
# 查找下一个节点
if self.trans_link_id:
next_node_id = record_id.workflow_id.node_ids.search([
('name', 'ilike', self.trans_link_id.name),
('workflow_id', '=', record_id.workflow_id.id)
])
# 流程委托:把选中的委托人解析为实际审批人(被委托人),双方都可见
delegation_targets = []
for selected_employee in self.employee_ids:
actual_employee = selected_employee
is_delegated = False
if selected_employee.user_id and selected_employee.user_id.id in entrust_substitution:
actual_employee = entrust_substitution[selected_employee.user_id.id]
is_delegated = True
delegation_targets.append((selected_employee, actual_employee, is_delegated))
for selected_employee, actual_employee, is_delegated in delegation_targets:
# 创建下一节点的审批记录(done_by 落库为被委托人)
self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": actual_employee.user_id.id if actual_employee.user_id else query_user_id,
"done_by_display": f"{actual_employee.name}(委托人)" if is_delegated else False,
"node_btn_name": "3",
"approval_state": "1", # 待审批
})
# 更新业务记录状态
related_record = self.env[self.res_model].browse(self.res_id)
if hasattr(related_record, 'self_users_ids'):
user_ids = [actual_employee.user_id.id for selected_employee, actual_employee, is_delegated in delegation_targets if actual_employee.user_id]
related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)]
record_id.write({'state': self.trans_link_id.name})
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
# 被委托人(实际经办) + 委托人(原选中人) 都可见
visible_uids = [actual_employee.user_id.id for selected_employee, actual_employee, is_delegated in delegation_targets if actual_employee.user_id]
for selected_employee, actual_employee, is_delegated in delegation_targets:
if is_delegated and selected_employee.user_id and selected_employee.user_id.id not in visible_uids:
visible_uids.append(selected_employee.user_id.id)
workflow_record_id.users_ids = [(4, uid) for uid in visible_uids]
workflow_record_id.employee_ids = [actual_employee.id for selected_employee, actual_employee, is_delegated in delegation_targets if actual_employee]
workflow_record_id.approve_state = "pending" # 待审批
for employee in self.circulated_employee_ids:
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
self.sudo().env["soong.workflow.record.copy"].create({
"code": record_id.code,
"name": record_id.name,
"res_model": self.res_model,
"workflow_id": self.node_btn_id.workflow_id.id,
"res_id": self.res_id,
"done_by": employee.user_id.id,
"approval_state_copy": "0",
})
if employee.user_id.id not in log_done_by_ids:
self.sudo().env["soong.workflow.log2"].create({
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee.user_id.id,
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
if self.circulated_employee_ids:
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
workflow_record_id.is_log2 = True
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
workflow_record_id.employee2_ids = [(6, 0, self.circulated_employee_ids.ids)]
title = record_id._description
if 'Room Booking' == title:
title = '会议通知'
for employee in self.circulated_employee_ids:
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form'
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=title + "传阅/抄送",
description=description, url=url,
btntxt="查看详情")
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id='18562027762',
title=title + '传阅/抄送,测试提醒',
description=description, url=url,
btntxt="查看详情")
self._handle_avoidance()
if self.node_btn_name == '退回':
if not self.return_link_id:
raise UserError('请选择退回节点')
record_id.write({'state': self.return_link_id.name})
next_node_id = record_id.workflow_id.node_ids.search([('name', '=', self.return_link_id.name), ('workflow_id', '=', record_id.workflow_id.id)])
for next_link in record_id.workflow_id.link_ids:
if next_link.name.split('-> ')[0].strip() == self.return_link_id.name.strip():
next_node_id = next_link.node_from
user_log = record_id.log_ids.filtered(
lambda log: log.done_by == self.env.user and log.approval_state in ['0', '1'])
user_log.write({'approval_state': '2',
"lase_click_date": fields.Datetime.now(),
"comment": self.comment,
"node_btn_name": "1"})
target_names = user_log.mapped('name')
logs_to_delete = record_id.log_ids.filtered(
lambda log: (log.name in target_names) and
not log.comment and
log not in user_log
)
if logs_to_delete:
logs_to_delete.unlink()
self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": self.employee_ids[0].user_id.id,
"approval_state": "0",
"node_btn_id": self.node_btn_id.id,
})
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
workflow_record_id.employee_ids = self.employee_ids
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
related_record = self.env[self.res_model].browse(self.res_id)
user_ids = [employee.user_id.id for employee in self.employee_ids if employee.user_id]
if hasattr(related_record, 'self_users_ids'):
related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)]
node_name = self.node_btn_id.node_id.name
description = f"您的{record_id.name}, 于当前{node_name}退回"
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form'
for employee in self.employee_ids:
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=record_id._description + "退回",
description=description, url=url, btntxt="查看详情")
if self.node_btn_name == '加签确认':
if hasattr(record_id, 'user_id') and record_id.user_id:
query_user_id = record_id.user_id
elif hasattr(record_id, 'users_id') and record_id.users_id:
query_user_id = record_id.users_id
description = f"标题:{record_id.name}\n申请人:{query_user_id.name}"
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
user_log = record_id.log_ids.filtered(
lambda log: log.done_by == self.env.user.id and log.node_btn_name != '1' and log.approval_state in ['0',
'1']
)
current_node_log = self.sudo().env["soong.workflow.log"].search(
[
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("done_by", "=", self.env.user.id),
("node_id", "=", self.node_btn_id.node_id.id),
],
order="create_date DESC",
limit=1
)
# 如果current_node_log 不存在的话 说明他加签是不返回 证明已经走了下一个节点 如果返回的话 节点是不会动的
if not current_node_log:
current_node_log1 = self.sudo().env["soong.workflow.log"].search(
[
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("done_by", "=", self.env.user.id),
("name", "=", "加签"),
("approval_state", "=", '1')
],
order="create_date DESC",
limit=1
)
current_node_log1.write({
"comment": self.comment,
"approval_state": "2", # 已完成
'document_ids': [(6, 0, self.document_ids.ids)],
"lase_click_date": fields.Datetime.now()
})
if current_node_log:
current_node_log.write({
"comment": self.comment,
"approval_state": "2", # 已完成
'document_ids': [(6, 0, self.document_ids.ids)],
"lase_click_date": fields.Datetime.now()
})
log_obj = self.sudo().env["soong.workflow.log"]
node_id = self.node_btn_id.node_id.id
# 从当前加签 log 上读取加签发起人
add_sign_user = current_node_log.add_sign_by
if not add_sign_user:
# 兼容旧数据:没有 add_sign_by 的,走原来的排序逻辑
last_sign_log = log_obj.search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("name", "=", "归档"),
], order="create_date DESC", limit=1)
if not last_sign_log:
last_sign_log = log_obj.search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
('approval_state', "=", '2'),
("name", "!=", "加签"),
], order="lase_click_date DESC")[0]
if not last_sign_log:
return
add_sign_user = last_sign_log.done_by
# 查找发起人在该节点上的原始 log(非加签),确定返回的 name(如"归档")
origin_log = log_obj.search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("node_id", "=", node_id),
("done_by", "=", add_sign_user.id),
("name", "!=", "加签"),
("approval_state", "=", "2"),
], order="create_date DESC", limit=1)
log_name = origin_log.name if origin_log and origin_log.name == '归档' else False
# 查找同一批次的加签 log(同一个发起人、同一个节点)
batch_logs = log_obj.search([
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("node_id", "=", node_id),
("name", "=", "加签"),
("add_sign_by", "=", add_sign_user.id),
])
# 是否都完成了
all_done = all(log.approval_state == "2" for log in batch_logs)
if all_done :
log_vals = {
"model": self.res_model,
"res_id": self.res_id,
"workflow_id": self.node_btn_id.workflow_id.id,
"node_id": node_id,
"done_by": add_sign_user.id,
"approval_state": "0",
"create_date": fields.Datetime.now(),
}
if log_name:
log_vals["name"] = log_name
log_obj.create(log_vals)
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
(
'res_model', '=', record_id._name)
])
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form'
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=add_sign_user.employee_ids.wecom_userid,
title=record_id._description + '加签返回',
description=description, url=url,
btntxt="查看详情")
if self.node_btn_name in ['结束', '确认']:
title = record_id._description
current_comment = self.comment or ''
if 'Room Booking' == record_id._description:
title = '会议通知'
is_work_remind = False
current_user = self.env.user
query_user_id = self.env.user.id
current_datetime = fields.Datetime.now()
is_admin = current_user.has_group('base.group_system')
current_node_log = self.sudo().env["soong.workflow.log"].search(
[
("model", "=", self.res_model),
("res_id", "=", self.res_id),
("done_by", "=", query_user_id),
("node_id", "=", self.node_btn_id.node_id.id),
],
order="create_date DESC",
limit=1
)
# 印章分组审批:章内串审,删除同章其他人的待办
if getattr(current_node_log, 'stamp_id', False):
same_stamp_logs = self.sudo().env["soong.workflow.log"].search(
[("model", "=", self.res_model),
("res_id", "=", self.res_id),
("id", "!=", current_node_log.id),
("node_id", "=", self.node_btn_id.node_id.id),
("stamp_id", "=", current_node_log.stamp_id.id),
("approval_state", "in", ['0', '1']),
])
for log_delete in same_stamp_logs:
if getattr(log_delete, 'msg_id', False):
self.sudo().env["wecom.apps"].back_message(log_delete.msg_id, category='approval')
same_stamp_logs.unlink()
# 归档判断
last_log = record_id.log_ids.sorted(
key=lambda r: (not r.lase_click_date, r.lase_click_date or False, r.id)
)[-1] if record_id.log_ids else None
all_user = True if last_log and last_log.done_by == current_user else False
if last_log:
same_name_logs = record_id.log_ids.filtered(
lambda log: log.name == last_log.name and log.node_btn_name == '4' and log.id != last_log.id
)
all_approved = all(log.approval_state in ['2', '3'] for log in same_name_logs) or not same_name_logs
if all_approved and all_user:
workflow_record = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=',record_id._name),
('reclaim_state', '=', False),
])
has_archive_btn = any(btn_name.name == '归档' for btn_name in self.node_btn_id.node_id.button_ids)
if has_archive_btn:
log_id = record_id.log_ids.filtered(lambda l: l.name == '归档')
employee_id = self.env['hr.employee'].sudo().search(
[('user_id', '=', record_id.log_ids[0].done_by.id)], limit=1)
if workflow_record:
workflow_record.write({
'approve_state': 'end',
'employee_ids': [(6, 0, [employee_id.id])],
'soong_state': 'to_active',
'node_id': False,
})
if not log_id:
self.env["soong.workflow.log"].create({
'name': '归档',
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": record_id.log_ids[0].done_by.id,
"approval_state": "0",
"node_btn_id": self.node_btn_id.id,
})
is_work_remind = True
else:
log_id.write({
'document_ids': [(6, 0, self.document_ids.ids)],
"approval_state": "2",
'lase_click_date': fields.Datetime.now(),
})
workflow_record.write({
'approve_state': 'end',
'soong_state': 'active',
'employee_ids': False,
})
else:
if workflow_record and self.node_btn_name == '结束':
workflow_record.sudo().write({
'approve_state': 'end',
'employee_ids': False,
'node_id': False,
'soong_state': 'done',
})
record_id.work_end = True
# 自动设置审批结果为 不通过
if any(keyword in current_comment for keyword in ['不同意', '不批准', '不通过']):
if hasattr(record_id, 'approval_result'):
record_id.approval_result = 'fail'
else:
if hasattr(record_id, 'approval_result'):
record_id.approval_result = 'pass'
self.node_btn_id.with_context(cx).run()
if is_admin:
user_log = record_id.log_ids.filtered(
lambda log: log.done_by == current_user and log.node_btn_name != '1' and log.approval_state == '0'
)
if user_log:
user_log.write({
'comment': self.comment,
'approval_state': "2",
'lase_click_date': current_datetime,
'document_ids': [(6, 0, self.document_ids.ids)]
})
else:
last_log = record_id.log_ids[-1] if record_id.log_ids else None
if last_log and last_log.approval_state not in ['2', '3']:
last_log.sudo().write({
'comment': self.comment,
'approval_state': "2",
'lase_click_date': fields.Datetime.now() - timedelta(hours=8),
'document_ids': [(6, 0, self.document_ids.ids)]
})
self.env['soong.workflow.record'].search([('code', '=', record_id.code)]).write({'approve_state': 'end',
"employee_ids": False,
"node_id": False, })
record_id.work_end = True
else:
if record_id.log_ids:
user_log = record_id.log_ids.filtered(
lambda log: log.done_by == current_user and log.node_btn_name != '1' and log.approval_state in ['0', '1']
)
if user_log:
user_log.write({
'comment': self.comment,
'approval_state': "2",
'lase_click_date': current_datetime,
'document_ids': [(6, 0, self.document_ids.ids)]
})
if any(log.node_btn_name == '3' for log in user_log):
other_node_logs = self.sudo().env["soong.workflow.log"].search(
[("model", "=", self.res_model),
("res_id", "=", self.res_id),
("id", "not in", user_log.ids),
("node_id", "=", self.node_btn_id.node_id.id)
])
for other_node_log in other_node_logs:
other_node_log.unlink()
if (record_id.log_ids2 or record_id.log_ids.filtered(lambda log: log.name == '承办')) and self.node_btn_name == '确认':
user_log2 = record_id.log_ids2.filtered(lambda log: log.done_by == current_user)
user_log1 = record_id.log_ids.filtered(lambda log: log.done_by == current_user)
if user_log2:
user_log2.write({
'commit': self.comment,
'approval_state': "2",
'lase_click_date': current_datetime,
})
if user_log1:
user_log1.write({
'comment': self.comment,
'approval_state': "2",
'lase_click_date': current_datetime,
})
for employee in self.circulated_employee_ids:
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&action={action_id.id}&model={self.res_model}&view_type=from&menu_id={record_id.id}'
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.circulated_employee_ids if employee.user_id]
workflow_record_id.employee_ids = [(4, employee.id)]
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
self.sudo().env["soong.workflow.record.copy"].create({
"code": record_id.code,
"name": record_id.name,
"res_model": self.res_model,
"workflow_id": self.node_btn_id.workflow_id.id,
"res_id": self.res_id,
"done_by": employee.user_id.id,
"approval_state_copy": "0",
})
if employee.user_id.id not in log_done_by_ids:
self.sudo().env["soong.workflow.log2"].create({
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee.user_id.id,
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=employee.wecom_userid,
title=record_id._description + "传阅/抄送",
description=description, url=url,
btntxt="查看详情")
# 给申请人发消息
if is_work_remind:
biz_record = self.env[self.res_model].browse(self.res_id)
if hasattr(biz_record, 'user_id') and biz_record.user_id:
employee = biz_record.user_id.employee_ids
elif hasattr(biz_record, 'users_id') and biz_record.users_id:
employee = biz_record.users_id.employee_ids
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
recipients = []
if self.node_btn_id.workflow_id.archive_type == 'applicant':
biz_record = self.env[self.res_model].browse(self.res_id)
if hasattr(biz_record, 'user_id') and biz_record.user_id:
employee = biz_record.user_id.employee_ids
elif hasattr(biz_record, 'users_id') and biz_record.users_id:
employee = biz_record.users_id.employee_ids
recipients.append(employee)
elif self.node_btn_id.workflow_id.archive_type == 'role':
role_ids = self.node_btn_id.workflow_id.role_ids
employees = self.env['hr.employee'].search([
'|',
('user_role_id', 'in', role_ids.ids),
('user_role_ids', 'in', role_ids.ids)
])
recipients = [emp for emp in employees]
else:
employees = self.node_btn_id.workflow_id.employee_ids
recipients = [emp for emp in employees]
for employee in recipients:
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&active_id={action_id.id}&model={record_id._name}&view_type=form'
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
if self.res_model == 'purchase.order':
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title='采购审批单',
description=f"{record_id.name}审批结束,可采购入库",
url=url, btntxt="查看详情")
else:
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=title,
description=f"标题为{record_id.name}的{title}已结束,请归档",
url=url, btntxt="查看详情")
self._handle_avoidance()
if self.node_btn_name == "分发":
record_id.is_send = True
menu_id = self.env['ir.ui.menu'].search([('name', '=', '行政收文')], limit=1)
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={menu_id.id}&active_id={action_id.id}&model={record_id._name}&view_type=form'
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
if self.employee_ids:
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.employee_ids if employee.user_id]
next_node_id = record_id.workflow_id.node_ids.search([('name', '=', '董事长'), ('workflow_id', '=', record_id.workflow_id.id)])
if record_id.log_ids:
record_id.log_ids[-1].write({"comment": self.comment, "approval_state": "2", "lase_click_date": fields.Datetime.now()})
log_id = self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": self.employee_ids.user_id.id,
"node_btn_name": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, self.employee_ids.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=self.employee_ids.wecom_userid, title=record_id._description, description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
log_id.msg_id = msg_id
for emp in self.dep_manager_employee_ids:
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.dep_manager_employee_ids if employee.user_id]
next_node_id = record_id.workflow_id.node_ids.search([('name', '=', '承办'), ('workflow_id', '=', record_id.workflow_id.id)])
log_id = self.sudo().env["soong.workflow.log"].create({
"code": self.code,
"model": self.res_model,
"res_id": self.res_id,
"node_id": next_node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": emp.user_id.id,
"node_btn_name": "4",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, emp.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=emp.wecom_userid, title=record_id._description + "承办", description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
log_id.msg_id = msg_id
for employee in self.circulated_employee_ids:
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.circulated_employee_ids if employee.user_id]
record_id = self.env[self.res_model].search([('id', '=', self.res_id)])
self.sudo().env["soong.workflow.record.copy"].create({
"code": record_id.code,
"name": record_id.name,
"res_model": self.res_model,
"workflow_id": self.node_btn_id.workflow_id.id,
"res_id": self.res_id,
"done_by": employee.user_id.id,
"approval_state_copy": "0",
})
if employee.user_id.id not in log_done_by_ids:
log2_domain = [
('model', '=', self.res_model),
('res_id', '=', self.res_id),
('node_id', '=', self.node_btn_id.node_id.id),
('done_by', '=', employee.user_id.id),
]
log2_id = self.sudo().env["soong.workflow.log2"].search(log2_domain, limit=1)
if not log2_id:
log2_id = self.sudo().env["soong.workflow.log2"].create({
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee.user_id.id,
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=employee.wecom_userid, title=record_id._description + "传阅/抄送",description=description, url=url, btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
log2_id.msg_id = msg_id
workflow_record_id.employee_ids = self.dep_manager_employee_ids + self.employee_ids + self.circulated_employee_ids
if record_id.log_ids:
record_id.log_ids[-1].write({"comment": self.comment, "approval_state": "2", "lase_click_date": fields.Datetime.now()})
if self.node_btn_name == "收回":
current_user = self.env.user
record = self.env[self.res_model].search([('id', '=', self.res_id)])
is_admin = current_user.has_group('base.group_system') or current_user.has_group('base.group_admin')
log_ids = record.log_ids.sorted('sequence')
last_normal_log = log_ids.filtered(lambda l: l.name != '加签').sorted('create_date')[-1:]
# 2. 如果有最后正常节点,只取【这个时间之后】的加签
if last_normal_log:
last_create_date = last_normal_log.create_date
has_sign_log = log_ids.filtered(lambda log:
log.name == '加签'
and log.node_id.name == record.state
and log.create_date > last_create_date # 关键:只取最后正常节点之后的加签
)
else:
has_sign_log = log_ids.filtered(lambda log: log.name == '加签' and log.node_id.name == record.state)
# 3. 判断这些加签是否已处理
if has_sign_log:
for log in has_sign_log:
if log.approval_state != '0':
raise UserError('加签流程已处理,不可收回!')
log_to_delete = has_sign_log
# 3. 删除后剩下的最后一条日志(取最后一个)
remaining_logs = log_ids - log_to_delete
if not remaining_logs:
raise UserError('无上级审批节点,无法收回')
prev_log = remaining_logs[-1] # 删除后的最后一条
# 4. 要删除的经办人 ID 列表
done_by_list = [log.done_by.id for log in log_to_delete]
else:
# 无加签日志
log_to_delete = log_ids.filtered(lambda log: log.name == record.state and log.node_btn_name != '1')
if self.res_model == 'yuthon.official.incoming.normal':
director_logs = log_ids.filtered(lambda log: '董事长' in (log.name or ''))
if len(director_logs) == 2:
log_to_delete = log_ids[-1:]
if not is_admin:
invalid_logs = log_to_delete.filtered(
lambda log: log.approval_state in ['1', '2'] and log.node_btn_name != '1'
)
if invalid_logs:
pass
total_log_count = len(log_ids)
deleted_log_count = len(log_to_delete)
prev_log_index = (total_log_count - deleted_log_count) - 1
other_log_index = total_log_count - deleted_log_count
if prev_log_index < 0 or prev_log_index >= len(log_ids):
raise UserError('无有效的上一级审批明细行,无法执行收回操作')
prev_log = log_ids[prev_log_index]
done_by_list = []
for index in range(other_log_index):
done_by_list.append(log_ids[index].done_by.id)
related_record = self.env[self.res_model].browse(self.res_id)
if hasattr(related_record, 'self_users_ids'):
related_record.self_users_ids = [(6, 0, [prev_log.done_by.id])]
# 核心写入:使用删除后的最后一条经办人
self.env['soong.workflow.record'].search([('code', '=', record.code)]).write({
"employee_ids": [(6, 0, [prev_log.done_by.employee_ids.id])],
"users_ids": [(6, 0, done_by_list)] if not has_sign_log else [(3, user_id) for user_id in done_by_list],
'soong_state': 'to_do'
})
# 消息回退 + 删除日志
for log_delete in log_to_delete:
self.sudo().env["wecom.apps"].back_message(log_delete.msg_id, category='approval')
log_to_delete.unlink()
if record.log_ids2:
for log_id2 in record.log_ids2:
self.sudo().env["wecom.apps"].back_message(log_id2.msg_id, category='approval')
record.log_ids2.unlink()
# 把最后一条日志置为处理中
prev_log.write({'approval_state': '1'})
# 回退状态
if prev_log.name != '归档':
record.write({
'state': prev_log.name,
'work_end': False,
})
if self.node_btn_name == '归档':
menu_id = self.env['ir.ui.menu'].search([('name', '=', '行政收文')], limit=1)
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
target_url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={menu_id.id}&active_id={action_id.id}&model={record_id._name}&view_type=form'
description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str(fields.Datetime.now() + timedelta(hours=8))
record = self.env[self.res_model].search([('id', '=', self.res_id)])
if self.res_model == 'purchase.order':
record.write({'purchase_state': 'done'})
if record.log_ids:
_last_log = record.log_ids[-1]
_last_log.write({'is_active_operate': True,
'lase_click_date': fields.Datetime.now(),
'approval_state': '1'
})
workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id),
('res_model', '=', record_id._name)
])
if record.log_ids:
_last_log = record.log_ids[-1]
_last_log.write({'approval_state': '2'})
if self.document_ids:
_last_log.write({'document_ids': [(6, 0, self.document_ids.ids)]})
_last_log.write({"comment": self.comment})
workflow_record_id.write({'soong_state': 'active',
'employee_ids':False
})
if self.circulated_employee_ids:
for employee in self.circulated_employee_ids:
if employee.user_id.id not in log_done_by_ids:
log2_domain = [
('model', '=', self.res_model),
('res_id', '=', self.res_id),
('node_id', '=', self.node_btn_id.node_id.id),
('done_by', '=', employee.user_id.id),
]
log2_id = self.sudo().env["soong.workflow.log2"].search(log2_domain, limit=1)
if not log2_id:
workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.circulated_employee_ids if employee.user_id]
log2_id = self.sudo().env["soong.workflow.log2"].create({
"model": self.res_model,
"res_id": self.res_id,
"node_id": self.node_btn_id.node_id.id,
"workflow_id": self.node_btn_id.workflow_id.id,
"done_by": employee.user_id.id,
"node_btn_id": self.node_btn_id.id,
"approval_state": "0",
})
url = self.sudo().env["wecom.apps"].generate_oauth_login_url(target_url, employee.user_id.id)
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=record_id._description + "传阅/抄送",
description=description,
url=url,
btntxt="查看详情")
if result_data and 'msgid' in result_data[-1]:
msg_id = result_data[-1]['msgid']
log2_id.msg_id = msg_id
if self.node_btn_name != "收回":
tree_id = self.env.ref("soong_workflow.soong_workflow_record_my_tree").id
kanban_id = self.env.ref("soong_workflow.soong_workflow_record_kanban").id
# self.red_reload()
return {
'type': 'ir.actions.client',
'tag': 'flush_badge_wkf',
'params': {
'tree_id': tree_id,
'kanban_id': kanban_id,
'uid': self.env.uid,
}
}
def _get_all_employee_partners(self):
all_employees = self.env['hr.employee']
all_employees |= self.employee_trans_domain_ids
all_employees |= self.employee_ids
all_employees |= self.circulated_employee_ids
all_employees |= self.dep_manager_employee_ids
all_employees |= self.add_employee_ids
all_employees |= self.supervising_employee_ids
all_employees |= self.employee_ids1
partners = all_employees.filtered(lambda e: e.user_id and e.user_id.partner_id).mapped('user_id.partner_id')
return partners
def update_self_users_ids(self):
related_record = self.env[self.res_model].browse(self.res_id)
if hasattr(related_record, 'self_users_ids'):
user_ids = [employee.user_id.id for employee in self.employee_ids if employee.user_id]
related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)]