人力资源

为什么普通员工能看同事档案却看不到私密字段:Odoo 的 Public Profile、自助修改与真实员工表边界

很多团队以为员工目录就是 hr.employee 的一个列表视图。Odoo 源码其实专门做了 hr.employee.public、SELF_READABLE_FIELDS 和用户自助同步,让“能看到谁、能改什么、真正数据落哪张表”成为三层边界。

Odoo 开发 人力资源
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 22 阅读

先说结论

很多人第一次看 Odoo HR,会以为员工目录就是把 hr.employee 直接展示给所有内部用户。

源码告诉你:并不是。

Odoo 实际上拆了三层:

  • 公开可见层hr.employee.public
  • 真实主数据层hr.employee
  • 本人自助编辑入口res.users 上一组和员工关联的字段

所以“我能看到同事”“我能改自己的电话”“HR 能不能看到私人信息”,并不是同一个问题。


为什么 Odoo 要单独做 hr.employee.public

hr.employee.public 不是一个普通模型,而是一个 SQL view

源码里它通过 hr_employee e 加上当前有效 hr.version v 拼出一张“公开员工档案视图”。这件事很关键,因为它说明 Odoo 的思路不是:

给所有人一点点 hr.employee 权限。

而是:

干脆单独准备一张公开视图,只暴露应该公开的字段。

这张公开视图里能看到的,主要是组织协作需要的内容,比如:

  • 姓名
  • 部门
  • 岗位
  • 工作邮箱、工作电话
  • 工作地点
  • 直属经理、下属
  • 头像
  • 在线/出勤状态

你会注意到,这些字段大多都服务于“认识同事、找到人、协作沟通”,而不是服务于“查看完整人事档案”。


为什么它不是把所有字段都复制过去

源码里 hr.employee 有一段很值得实施顾问反复看:

  • 如果当前用户有 HR 读权限,就直接读 hr.employee
  • 如果没有,就走 search_fetch() / fetch() 的 Hack 路径
  • 这条路径会去读 hr.employee.public
  • 然后只把公开字段复制到 hr.employee 的缓存里

这个设计非常妙。

它的效果是:

  • 普通用户依然像是在“搜索员工”
  • 但底层其实只拿到了公开字段
  • 一旦你去读私密字段,就直接触发访问错误

也就是说,Odoo 不是靠“前端别显示某些字段”来做安全,而是连 ORM 读取口径 都分开了。


私密字段为什么不是“隐藏一下就行”

源码里的 _check_private_fields() 很直接:

如果你请求的字段不在 hr.employee.public 里,就抛 AccessError

这背后的含义很现实:

  • 住址
  • 紧急联系人
  • 银行账户
  • 证件信息
  • 薪酬相关数据

这些不应该只是“默认折叠”,而应该是 默认不可读

很多系统做权限时喜欢靠界面层遮挡,但只要 API 或导出没控住,最后还是会漏。Odoo 在员工档案这里显然吸取了教训:公开资料和私密资料必须是模型级边界。


那员工怎么改自己的私人资料

这里就进入第三层了:res.users

/home/ubuntu/odoo-temp/addons/hr/models/res_users.py 里,Odoo 定义了两组字段:

  • HR_READABLE_FIELDS
  • HR_WRITABLE_FIELDS

并把它们并入:

  • SELF_READABLE_FIELDS
  • SELF_WRITEABLE_FIELDS

这意味着什么?

意思是:

普通员工虽然没有 HR 管理权限,但可以通过“我的偏好 / 我的资料”安全地读写属于自己的某些员工字段。

这些字段包括:

  • private_street
  • private_phone
  • private_email
  • emergency_contact
  • emergency_phone
  • mobile_phone
  • work_email
  • work_location_id
  • barcode
  • pin

也就是说,Odoo 没有让员工直接去编辑完整 hr.employee 表单,而是给了一个 受控的自助入口


为什么“我的资料”放在 res.users,数据却落到 hr.employee

看起来有点绕,但业务上很合理。

res.users 是“账号主体”,而很多自助编辑动作都是从用户自己的偏好页发起的;可真正的人力主数据又应该沉淀在 hr.employee

所以 Odoo 采用了 related 字段:

  • res.users.work_phone -> employee_id.work_phone
  • res.users.private_email -> employee_id.private_email
  • res.users.work_location_id -> employee_id.work_location_id

这等于给员工一个“从用户侧改员工字段”的窗口。

它不是双表重复存储,而是:

  • 入口在 res.users
  • 主档在 hr.employee

这就兼顾了“用户自助”与“HR 主数据归口”。


为什么还要有 _sync_user() 这种同步逻辑

hr.employee 里,_sync_user() 会把这些信息和用户联动:

  • work_contact_id
  • user_id
  • 头像
  • 时区 tz

创建员工时,如果指定了 user_id,源码会:

  1. _sync_user() 补齐员工上的联动值
  2. 必要时移除旧的 work_contact_id
  3. 保证同一公司里一个用户只绑定一个员工

这里最值得记住的一点是:

账号和员工是关联关系,不是完全相同的对象。

所以需要同步,但不能偷懒地把两者混成一张表。


为什么普通用户还能打开员工详情页

源码里连 get_formview_id()get_formview_action() 都做了分流:

  • HR 用户打开的是 hr.employee 表单
  • 普通内部用户打开的是 hr.employee.public 表单

这背后其实是一个很成熟的产品判断:

用户想看“员工信息”,不代表他应该进入“员工主数据维护界面”。

于是同样是点进一个同事:

  • HR 看到的是管理界面
  • 普通员工看到的是公开档案

体验上很顺,权限上也很稳。


这套设计解决了哪些现实问题

1. 员工目录可以开放,但不会把人事档案全暴露

组织通讯录需要可见性,人事隐私需要边界。

2. 自助维护可以下放,但不会绕过主数据归口

员工能改自己的私人电话和紧急联系人,不代表他拿到了 HR 全权限。

3. 账号对象和员工对象既联动又不混淆

这样以后做:

  • 多公司
  • 一个用户对应一个公司员工
  • 公开档案
  • 离职归档

都比较容易保持一致性。


实施时最容易踩的坑

坑一:把员工目录权限直接开到 hr.employee

短期省事,长期高风险。

坑二:把“我的偏好”当成只改 res.users

其实很多字段最终落的是 hr.employee,如果定制时没理解 related 链路,很容易改坏。

坑三:以为公开档案只是一个简化视图

不是。它本质上是 Odoo 专门做出来的 安全边界模型


一句话记忆

Odoo 不是把 hr.employee 随便裁掉几列给大家看,而是用 hr.employee.public 管公开可见,用 res.users 管本人自助,用 hr.employee 管真实主档。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。