diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 7e29c7be2..26e8ca35c 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -69,6 +69,18 @@ class SoongWorkflowRecord(models.Model): soong_state = fields.Selection([('to_do', '待办'), ('done', '已办'),('to_active', '待归档'), ('active', '已归档')], string="流程状态", default="to_do") member_of_department = fields.Boolean("是否是本部门数据", compute='_compute_part_of_department', search='_search_part_of_department') + special_member_visible = fields.Boolean( + "特殊权限可见(人事/考勤)", + compute='_compute_special_member_visible', + search='_search_special_member_visible', + help="当前用户属于『本部-角色-人事专员』或『考勤-用户』用户组时,可查看对应的(仅本公司的)人事/考勤流程", + ) + dept_query_visible = fields.Boolean( + "部门查询可见(排除考勤/人事)", + compute='_compute_dept_query_visible', + search='_search_dept_query_visible', + help="本部门可见,但排除审批分组为考勤/人事的流程(二者仅对特殊权限组可见)", + ) # 人力资源流程的发生日期 workflow_happen_date = fields.Date(string="发生日期") @@ -613,6 +625,78 @@ class SoongWorkflowRecord(models.Model): domain = ['!'] + domain return domain + # ------------------------------------------------------------------ + # 特殊权限可见:人事专员 / 考勤-用户 用户组可查看对应(仅本公司)流程 + # ------------------------------------------------------------------ + @api.depends_context('uid', 'company') + @api.depends('approvals_group_id', 'company_id') + def _compute_special_member_visible(self): + user = self.env.user + hr_group = self.env['res.groups'].sudo().search([('name', '=', '本部-角色-人事专员')], limit=1) + kq_group = self.env['res.groups'].sudo().search([('name', '=', '考勤-用户')], limit=1) + is_hr_user = bool(hr_group and user in hr_group.users) + is_kq_user = bool(kq_group and user in kq_group.users) + active_company_ids = self.env.companies.ids + for record in self: + visible = False + if record.approvals_group_id and record.company_id.id in active_company_ids: + group_name = record.approvals_group_id.name + if group_name == '人事' and is_hr_user: + visible = True + elif group_name == '考勤' and is_kq_user: + visible = True + record.special_member_visible = visible + + def _search_special_member_visible(self, operator, value): + user = self.env.user + hr_group = self.env['res.groups'].sudo().search([('name', '=', '本部-角色-人事专员')], limit=1) + kq_group = self.env['res.groups'].sudo().search([('name', '=', '集团-角色-考勤')], limit=1) + is_hr_user = bool(hr_group and user in hr_group.users) + is_kq_user = bool(kq_group and user in kq_group.users) + company_ids = self.env.companies.ids + + # 注意:必须用扁平前缀形式(&/| 放在 domain 顶层), + # 不能把 ['&', ...] 作为子列表元素,否则 Odoo 会把它当叶子、把 & 当成字段名报错。 + parts = [] + if is_hr_user: + parts.append(['&', ('approvals_group_id.name', '=', '人事'), ('company_id', 'in', company_ids)]) + if is_kq_user: + parts.append(['&', ('approvals_group_id.name', '=', '考勤'), ('company_id', 'in', company_ids)]) + + if not parts: + # 当前用户不属于任何特殊权限组,恒不满足 + domain = [('id', '=', False)] + else: + domain = ['|'] * (len(parts) - 1) + for grp in parts: + domain += grp + + if (operator == '=' and not value) or (operator == '!=' and value): + domain = ['!'] + domain + return domain + + # ------------------------------------------------------------------ + # 部门查询可见(排除考勤/人事):本部门数据 且 审批分组不是考勤/人事 + # ------------------------------------------------------------------ + @api.depends_context('uid', 'company') + @api.depends('member_of_department', 'approvals_group_id') + def _compute_dept_query_visible(self): + for record in self: + record.dept_query_visible = bool( + record.member_of_department + and record.approvals_group_id + and record.approvals_group_id.name not in ('考勤', '人事') + ) + + def _search_dept_query_visible(self, operator, value): + # 本部门数据 且 审批分组不是考勤/人事(member_of_department 由 search 方法内联展开,无嵌套) + domain = ['&', + ('approvals_group_id.name', 'not in', ['考勤', '人事']), + ('member_of_department', '=', True)] + if (operator == '=' and not value) or (operator == '!=' and value): + domain = ['!'] + domain + return domain + @api.model def create(self, vals_list): res = super().create(vals_list) diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index ec2c51da2..63a885a48 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -459,9 +459,15 @@ ir.actions.act_window soong.workflow.record tree,kanban,search - - - [('reclaim_state', '=', False), ('approve_state','!=','start'), '|', ('create_uid', '=', uid), ('users_ids', 'in', [uid])] + [ + ('reclaim_state', '=', False), + ('approve_state', '!=', 'start'), + '|', + '|', + ('users_ids', 'in', [uid]), + ('dept_query_visible', '=', True), + ('special_member_visible', '=', True) + ] {'search_default_my_department_shifts': True, 'default_order': 'display_end_time desc'} diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index 8797141ef..47470cb12 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -48,7 +48,11 @@ class SoongWorkflowCommentWizard(models.TransientModel): 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_ids = fields.Many2many('hr.employee', 'add_employee_rel', 'custom_id', 'employee_id', string="加签人员", help='点击确认以后评论的人员', store=True, domain="[('id', 'in', add_employee_filter_ids)]") + 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='并:任何人均给意见,最后一个完成的人转交到下一节点.' @@ -133,6 +137,35 @@ class SoongWorkflowCommentWizard(models.TransientModel): ]) 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: @@ -754,7 +787,7 @@ class SoongWorkflowCommentWizard(models.TransientModel): # 是否都完成了 all_done = all(log.approval_state == "2" for log in batch_logs) # 如果都完成 → 创建一条新日志给加签发起人 - if all_done: + if all_done and self.is_return: log_vals = { "model": self.res_model, "res_id": self.res_id, diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml index b6a1e9549..daa79a326 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml +++ b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml @@ -72,9 +72,10 @@