194 lines
8.9 KiB
Python
194 lines
8.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
from odoo import api, fields, models, _
|
||
from pypinyin import pinyin, lazy_pinyin
|
||
from odoo.osv import expression
|
||
from odoo.exceptions import UserError, ValidationError
|
||
|
||
class YuthonUserRole(models.Model):
|
||
_name = 'soong.user.role'
|
||
_order = 'number asc'
|
||
|
||
name = fields.Char(string="角色名称")
|
||
number = fields.Char(string="序号")
|
||
is_main = fields.Boolean(string="主角色")
|
||
department_id = fields.Many2one('hr.department', string="所属部门")
|
||
create_time = fields.Date(string="生效日期", default=fields.Datetime.today())
|
||
end_time = fields.Date(string="终止日期")
|
||
parent_id = fields.Many2one('soong.user.role', string='上级角色')
|
||
user_role_group_ids = fields.Many2many('soong.user.role.group', string='角色分组', index=True)
|
||
personnel_ids = fields.Many2many('hr.employee', string='在岗人员', store=True, index=True)
|
||
number_people = fields.Integer(string="在岗人数", compute='_compute_employee_ids')
|
||
personnel = fields.Text(string="在岗人员")
|
||
employee_ids = fields.One2many('hr.employee', 'user_role_id')
|
||
company_id = fields.Many2one('res.company', string="公司", default=lambda self: self.env.company)
|
||
pinyin_name = fields.Char(string="拼音名称", compute='_compute_pinyin', index=True)
|
||
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company', search='_search_part_of_company')
|
||
|
||
@api.depends_context('uid', 'company')
|
||
@api.depends('company_id')
|
||
def _compute_is_company(self):
|
||
active_company_ids = self.env.companies
|
||
for employee in self:
|
||
employee.is_company = employee.company_id in active_company_ids
|
||
|
||
def _search_part_of_company(self, operator, value):
|
||
if operator not in ('=', '!=') or not isinstance(value, bool):
|
||
raise UserError(('Operation not supported'))
|
||
company_ids = self.env.companies.ids # 获取公司ID列表
|
||
# 处理反向操作符
|
||
if not value:
|
||
operator = '!=' if operator == '=' else '='
|
||
# 使用company_id in company_ids的结构
|
||
if operator == '=':
|
||
return [('company_id', 'in', company_ids)]
|
||
else: # operator == '!='
|
||
return [('company_id', 'not in', company_ids)]
|
||
|
||
@api.onchange('is_main')
|
||
def _onchange_is_main(self):
|
||
for employee in self.personnel_ids:
|
||
employee_id = self.env['hr.employee'].search([('id', '=', employee.id.origin)])
|
||
if self.is_main:
|
||
# 1. 是主角色:替换主角色,清空辅助角色中的当前记录
|
||
employee_id.user_role_id = self.id.origin
|
||
if self.id.origin in employee_id.user_role_ids.ids:
|
||
employee_id.user_role_ids = [(3, self.id.origin)]
|
||
else:
|
||
# 2. 不是主角色:检查辅助角色,没有则添加
|
||
if self.id.origin not in employee_id.user_role_ids.ids:
|
||
employee_id.user_role_ids = [(4, self.id.origin)] #
|
||
|
||
@api.depends('department_id')
|
||
def _compute_display_name(self):
|
||
for record in self:
|
||
display_parts = []
|
||
if record.name:
|
||
display_parts.append(record.name)
|
||
if record.department_id:
|
||
display_parts.append(f"({record.department_id.name})")
|
||
record.display_name = " ".join(display_parts)
|
||
|
||
@api.depends('name')
|
||
def _compute_pinyin(self):
|
||
for employee in self:
|
||
employee.pinyin_name = ''.join(lazy_pinyin(employee.name))
|
||
|
||
@api.model
|
||
def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None):
|
||
domain = domain or []
|
||
if name:
|
||
has_chinese = any('\u4e00' <= char <= '\u9fff' for char in name)
|
||
if has_chinese:
|
||
# 包含中文:使用模糊匹配
|
||
search_domain = [('name', operator, name)]
|
||
else:
|
||
# 纯字母/数字:使用精确匹配
|
||
search_domain = ['|',
|
||
('pinyin_name', operator, name),
|
||
('name', '=', name)]
|
||
domain = expression.AND([search_domain, domain])
|
||
return self._search(domain, limit=limit, order=order)
|
||
|
||
@api.depends('personnel_ids')
|
||
def _compute_employee_ids(self):
|
||
for rec in self:
|
||
rec.number_people = 0
|
||
if rec.personnel_ids:
|
||
rec.number_people = len(rec.personnel_ids)
|
||
|
||
@api.onchange('personnel_ids')
|
||
def _onchange_personnel_ids(self):
|
||
for personnel_id in self.personnel_ids:
|
||
employee = self.env['hr.employee'].browse(personnel_id.id)
|
||
if employee:
|
||
if self.id not in employee.user_role_ids.ids:
|
||
new_user_role_ids = list(set(employee.user_role_ids.ids + [self.id]))
|
||
employee.with_context(_syncing_role_personnel=True).write({
|
||
'user_role_ids': [(6, 0, new_user_role_ids)],
|
||
})
|
||
|
||
def write(self, vals):
|
||
"""覆盖 write,personnel_ids 变更时双向同步到员工的主/辅助角色字段"""
|
||
# 记录变更前的 personnel_ids
|
||
old_personnel_map = {rec.id: set(rec.personnel_ids.ids) for rec in self}
|
||
result = super().write(vals)
|
||
if 'personnel_ids' not in vals:
|
||
return result
|
||
for rec in self:
|
||
new_ids = set(rec.personnel_ids.ids)
|
||
old_ids = old_personnel_map.get(rec.id, set())
|
||
added_ids = new_ids - old_ids # 新增的员工
|
||
removed_ids = old_ids - new_ids # 移除的员工
|
||
if rec.is_main:
|
||
# 主角色:新增员工 → 设为主角色;移除员工 → 清空主角色(若仍是该角色)
|
||
for emp in self.env['hr.employee'].browse(list(added_ids)):
|
||
emp.write({'user_role_id': rec.id})
|
||
for emp in self.env['hr.employee'].browse(list(removed_ids)):
|
||
if emp.user_role_id.id == rec.id:
|
||
emp.write({'user_role_id': False})
|
||
else:
|
||
# 辅助角色:新增员工 → 添加到辅助角色;移除员工 → 从辅助角色移除
|
||
for emp in self.env['hr.employee'].browse(list(added_ids)):
|
||
if rec.id not in emp.user_role_ids.ids:
|
||
emp.write({'user_role_ids': [(4, rec.id)]})
|
||
for emp in self.env['hr.employee'].browse(list(removed_ids)):
|
||
if rec.id in emp.user_role_ids.ids:
|
||
emp.write({'user_role_ids': [(3, rec.id)]})
|
||
return result
|
||
|
||
class YuthonUserRoleGroup(models.Model):
|
||
_name = 'soong.user.role.group'
|
||
_order = 'number asc'
|
||
_rec_name = 'name'
|
||
|
||
name = fields.Char(string="角色名称")
|
||
number = fields.Char(string="序号", store=True)
|
||
user_note = fields.Text(string="角色说明")
|
||
user_role_ids = fields.Many2many('soong.user.role', string='包含角色', index=True)
|
||
company_id = fields.Many2one('res.company', string='公司', default=lambda self: self.env.company)
|
||
start_date = fields.Date(string="生效日期", default=fields.Datetime.today())
|
||
end_date = fields.Date(string="终止日期")
|
||
create_date = fields.Date(string="创建日期", default=fields.Datetime.today())
|
||
|
||
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',
|
||
search='_search_part_of_company')
|
||
|
||
@api.depends_context('uid', 'company')
|
||
@api.depends('company_id')
|
||
def _compute_is_company(self):
|
||
active_company_ids = self.env.companies
|
||
for record in self:
|
||
record.is_company = record.company_id in active_company_ids
|
||
|
||
def _search_part_of_company(self, operator, value):
|
||
if operator not in ('=', '!=') or not isinstance(value, bool):
|
||
raise UserError(('Operation not supported'))
|
||
company_ids = self.env.companies.ids
|
||
if not value:
|
||
operator = '!=' if operator == '=' else '='
|
||
if operator == '=':
|
||
return [('company_id', 'in', company_ids)]
|
||
else:
|
||
return [('company_id', 'not in', company_ids)]
|
||
|
||
def button_role_group_wizard(self):
|
||
return {
|
||
'name': '包含角色弹窗',
|
||
'type': 'ir.actions.act_window',
|
||
'view_mode': 'form',
|
||
'res_model': 'role.group.wizard',
|
||
'target': 'new',
|
||
'context': {
|
||
'default_role_group_id': self.id,
|
||
'default_user_role_ids': [(6, 0, self.user_role_ids.ids)]
|
||
}
|
||
}
|
||
|
||
|
||
class YuthonRoleTree(models.Model):
|
||
_name = 'soong.role.tree'
|
||
_order = 'number asc'
|
||
|
||
name = fields.Char(string="角色名称")
|
||
department_id = fields.Many2one('hr.department', string="所属部门")
|
||
company_id = fields.Many2one('res.company', string="公司", index=True, default=lambda self: self.env.company) |