# -*- 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='跳过相同人员以及无审批人员', compute='_compute_invalid_link_ids', 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) 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) is_this_company = fields.Boolean('本公司', default=True) employee_ids1 = fields.Many2many('hr.employee', 'employee_ids1_rel', 'custom_id1', 'employee_id1', string="人员范围", store=True) @api.onchange('is_selection') def _onchange_is_selection(self): domain = [('id', 'in', self.employee_ids1.ids)] if self.is_selection: if self.is_this_company: domain.append(('company_id', '=', self.company_id.id)) all_employees = self.env['hr.employee'].search(domain) self.employee_ids = all_employees else: self.employee_ids = False @api.onchange('employee_ids1') def _onchange_employee_ids1(self): filtered_employees = self.employee_ids1 if self.is_this_company: filtered_employees = self.employee_ids1.filtered( lambda emp: emp.company_id.id == self.company_id.id ) if len(filtered_employees) == 1: self.employee_ids = filtered_employees else: domain = [('id', 'in', self.employee_ids1.ids)] if self.is_selection: if self.is_this_company: domain.append(('company_id', '=', self.company_id.id)) all_employees = self.env['hr.employee'].search(domain) self.employee_ids = all_employees else: self.employee_ids = False @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 @api.depends('trans_link_id') def _compute_invalid_link_ids(self): for record in self: invalid_links = self.env['trans.link'] ctx_links = self.env.context.get('all_workflow_links', []) all_links = self.env['trans.link'].browse(ctx_links) if isinstance(ctx_links, (list, tuple)) else ctx_links record_id = self.env[record.res_model].browse(record.res_id) if record.res_model and record.res_id else None current_link = record.trans_link_id if current_link and all_links: sorted_all_links = all_links.sorted('sequence') for idx, link in enumerate(sorted_all_links): approvers = self._get_approvers_for_link(link, record_id) # ====================== 逻辑1:跳过无审批人的节点 if link.id == current_link.id and link.sequence == current_link.sequence: # 获取已审批人员 log_records = record_id.log_ids approved_users = log_records.filtered( lambda log: log.node_btn_name != '1' and log.done_by).mapped('done_by.employee_ids') approved_employee_ids = approved_users.ids employee_ids = approvers.ids if len(approvers) > 1: is_all_included = all(emp_id in approved_employee_ids for emp_id in employee_ids) if is_all_included: if idx + 1 < len(sorted_all_links): record.trans_link_id = sorted_all_links[idx + 1] invalid_links |= current_link break # ====================== 逻辑2:单人审批 → 按规则跳过同审批人节点 ====================== if len(approvers) == 1 and record_id: current_approver = approvers[0] # 查找对应节点配置 node_id = record_id.workflow_id.node_ids.search([ ('name', '=', link.name), ('workflow_id', '=', record_id.workflow_id.id) ], limit=1) # 加 limit=1 更严谨 if node_id: # 跳过规则:next → 跳过【下一个】同审批人节点 if node_id.same_user_skip_type == 'next': if idx + 1 < len(sorted_all_links): next_link = sorted_all_links[idx + 1] next_approvers = self._get_approvers_for_link(next_link, record_id) if len(next_approvers) == 1 and next_approvers[0] == current_approver: invalid_links |= next_link # 跳过规则:prev → 跳过【上一个】同审批人节点 if node_id.same_user_skip_type == 'prev': if idx - 1 >= 0: prev_link = sorted_all_links[idx - 1] prev_approvers = self._get_approvers_for_link(prev_link, record_id) if len(prev_approvers) == 1 and prev_approvers[0] == current_approver: invalid_links |= prev_link # 最终赋值 record.invalid_link_ids = invalid_links @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', []) all_links = self.env['trans.link'].browse(ctx_links) if isinstance(ctx_links, (list, tuple)) else ctx_links valid_links = all_links - record.invalid_link_ids record.is_selection = False if self.node_btn_name == '退回': record.is_this_company = False else: record.is_this_company = True 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 == '印章管理员': 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 = self._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_automatic_group: record.is_this_company = False if button.is_node_automatic: record.is_selection = True record._onchange_is_selection() def _get_approvers_for_link(self, link, record_id=None): """ 参数: link: trans.link 记录对象 record_id: 关联的业务记录对象 返回: hr.employee 记录集(可能为空) """ if not link or isinstance(link, int): return self.env['hr.employee'] 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: found_target_btn = False for btn_name in button_priority_list: for btn in sorted(node.button_ids, key=lambda x: x.sequence): if btn.name == btn_name: is_in_scope = True if not btn.is_review_invisible: button = btn found_target_btn = True 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: button = btn found_target_btn = True break if found_target_btn: break if not button: return self.env['hr.employee'] link_users_id = button.users_role_id link_users_group_ids = button.users_role_group_ids is_automatic = button.is_automatic is_automatic_company = button.is_automatic_company is_automatic_group = button.is_automatic_group is_logged_by = button.is_logged_by is_logged_company = button.is_logged_company is_logged_group = button.is_logged_group is_dep = button.is_dep is_field = button.specify_field_id if button.reviewer_id: employee = self.env['hr.employee'].search([ ('user_id', '=', button.reviewer_id.id), ], limit=1) return employee # 部门和公司筛选条件 department_domain = [] company_domain = [] user_role_domain = [] if record_id: # 获取当前用户和申请人的相关信息 current_user = self.env.user applicant = None # 获取申请人的用户对象 for field_name in ['user_id', 'users_id']: if hasattr(record_id, field_name): applicant = getattr(record_id, field_name) if applicant: break # 根据勾选的条件设置域条件(只勾选一个) if is_automatic and record_id.department_id: dept_id = record_id.department_id.id # 按当前申请人自动匹配-本部门(保留部门和公司条件) applicant_dept_ids = applicant.employee_ids.department_ids.ids department_domain = [ '|', '|', '|', ('department_id', '=', dept_id), ('department_ids', 'in', [dept_id]), ('department_id', 'in', applicant_dept_ids), ('department_ids', 'in', applicant_dept_ids), ] company_domain = [('company_id', '=', applicant.company_id.id)] elif is_automatic_company and applicant and hasattr(applicant, 'company_id'): # 按当前申请人自动匹配-本公司(仅保留公司条件) company_id = applicant.company_id.id company_domain = [('company_id', '=', company_id)] elif is_automatic_group and applicant and hasattr(applicant, 'company_id'): # 按当前申请人自动匹配-本集团(仅保留公司条件) parent_company = applicant.company_id.parent_id if parent_company: group_company_ids = self.env['res.company'].search([('id', 'child_of', parent_company.id)]).ids company_domain = [('company_id', 'in', group_company_ids)] elif is_dep and hasattr(record_id, 'department_id1') and record_id.department_id1: department_id1 = record_id.department_id1.id department_domain = ['|', ('department_id', '=', department_id1), ('department_ids', 'in', department_id1)] user_role_domain = ['|', ('user_role_id', '=', button.role_id.user_role_ids.ids), ('user_role_ids', 'in', button.role_id.user_role_ids.ids)] elif is_logged_by and current_user.department_id: # 按当前登录人自动匹配-本部门(保留部门和公司条件) department_domain = ['|', ('department_id', '=', current_user.department_id.id), ('department_ids', 'in', current_user.department_id.id)] company_domain = [('company_id', '=', current_user.company_id.id)] elif is_logged_company: # 按当前登录人自动匹配-本公司(仅保留公司条件) company_id = current_user.company_id.id company_domain = [('company_id', '=', company_id)] elif is_logged_group: # 按当前登录人自动匹配-本集团(仅保留公司条件) parent_company = current_user.company_id.parent_id if parent_company: group_company_ids = self.env['res.company'].search([('id', 'child_of', parent_company.id)]).ids company_domain = [('company_id', 'in', group_company_ids)] elif is_field: field_name = button.specify_field_id.name field_value = record_id[field_name] if field_value: if button.specify_field_id.relation == 'res.users': employee = self.env['hr.employee'].sudo().search( [('user_id', '=', field_value.id)], limit=1 ) if employee: return employee elif button.specify_field_id.relation == 'hr.employee': return field_value else: department_domain = ['|', ('department_id', '=', field_value.id), ('department_ids', 'in', field_value.id)] if link_users_id: role_domain = ['|', ('user_role_id', '=', link_users_id.id), ('user_role_ids', 'in', link_users_id.id)] if user_role_domain: role_domain = ['|'] + role_domain + user_role_domain domain = role_domain + company_domain + department_domain return self.env['hr.employee'].sudo().search(domain) elif link_users_group_ids: role_domain = ['|', ('user_role_id', 'in', link_users_group_ids.user_role_ids.ids), ('user_role_ids', 'in', link_users_group_ids.user_role_ids.ids),] if user_role_domain: role_domain = ['|'] + role_domain + user_role_domain domain = role_domain + company_domain + department_domain return self.env['hr.employee'].sudo().search(domain) # 特殊分发按钮 if '分发' in link.name and record_id and hasattr(record_id, 'users_id'): return record_id.users_id.employee_id return self.env['hr.employee'] # 默认返回空记录集 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 == '转交': 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: 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: other_node_log.unlink() # 检查是否为并审模式 if self.approval_concatenation == 'bing': # 并审模式考勤异常 expected_employees = self.employee_ids if record_id.log_ids2: log_employee_ids = record_id.log_ids2.mapped('done_by.employee_id.id') self.employee_ids = expected_employees.filtered(lambda emp: emp.id not in log_employee_ids) # 查找下一个节点 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 self.employee_ids: # 检查该员工在下一节点是否已有记录 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, "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) ]) workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.employee_ids if employee.user_id] workflow_record_id.employee_ids = self.employee_ids.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 = [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,)] if not self.trans_link_id.name: raise UserError('无转交节点') record_id.write({'state': self.trans_link_id.name}) for employee in self.employee_ids: 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: # 串审模式 (沿用原有逻辑) # 查找下一个节点 next_node_id = record_id.workflow_id.node_ids.search([ ('name', 'ilike', self.trans_link_id.name), ('workflow_id', '=', record_id.workflow_id.id) ]) for employee_id in self.employee_ids: # 创建下一节点的审批记录 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_id.user_id.id if self.employee_ids else query_user_id, "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 = [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,)] 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, employee.user_id.id) for employee in self.employee_ids if employee.user_id] workflow_record_id.employee_ids = self.employee_ids.ids 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)] 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=record_id._description + "传阅/抄送", description=description, url=url, btntxt="查看详情") self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id='18562027762', title=record_id._description + '传阅/抄送,测试提醒', description=description, url=url, btntxt="查看详情") if self.node_btn_name == '退回': if not self.return_link_id: raise UserError('请选择退回节点') if self.res_model == 'issue.approval': if self.return_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 = 'draft' 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 in ['结束', '确认']: title = record_id._description if 'Room Booking' == record_id._description: title = '会议通知' is_work_remind = False current_user = self.env.user current_datetime = fields.Datetime.now() is_admin = current_user.has_group('base.group_system') 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 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 user_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", "!=", user_log.id), ("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 not record_id.log_ids2.search([('approval_state', '=', '0')]) or record_id.log_ids2: last_log = record_id.log_ids[-1] if record_id.log_ids else None 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: workflow_record = self.env['soong.workflow.record'].search([('code', '=', record_id.code)], limit=1) 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({ 'soong_state': 'active', 'employee_ids': False, }) else: if workflow_record: workflow_record.sudo().write({ 'approve_state': 'end', 'employee_ids': False, 'node_id': False, 'soong_state': 'done', }) record_id.work_end = True # 给申请人发消息 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.node_btn_id.with_context(cx).run() 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)]) 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 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') log_to_delete = log_ids.filtered(lambda log: log.name == record.state) 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: raise UserError('当前状态不可以收回') 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] related_record = self.env[self.res_model].browse(self.res_id) done_by_list = [] for index in range(other_log_index): done_by_list.append(log_ids[index].done_by.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)], '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'}) 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'}) record.log_ids[-1].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 not self.circulated_employee_ids: record.log_ids[-1].write({'approval_state': '2'}) if self.document_ids: record.log_ids[-1].write({'document_ids': [(6, 0, self.document_ids.ids)]}) record.log_ids[-1].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,)]