如果你做过多公司项目,应该见过这种现象:
- 同一个用户;
- 同一段代码;
- 切换公司以后,看到的记录突然不一样了。
这不是偶然,而是 Odoo 记录规则本来就把公司上下文放进了计算结果里。
从 odoo/addons/base/models/ir_rule.py 看,ir.rule 的域不是每次都重新算的,它是带缓存的。而这个缓存最关键的上下文键之一,就是 allowed_company_ids。
一、_compute_domain() 是缓存过的
_compute_domain() 不是一个普通函数。它被 ormcache 包起来,并且缓存键里会用到:
self.env.uidself.env.sumodel_namemode- 以及
_compute_domain_context_values()返回的上下文值
而 _compute_domain_context_values() 默认只返回:
allowed_company_ids
这意味着什么?
意味着同一个用户切换公司后,哪怕模型和操作类型没变,缓存键也可能变,因此规则域会重新计算。
二、为什么只看 allowed_company_ids
这里的设计很有意思。
Odoo 并不是把所有 context 键都塞进缓存键里,而是只挑了真正会影响规则结果的关键项。
这也符合业务直觉:
- 公司切换会改变可见记录;
- 但很多其他 context 值并不会影响记录规则结果;
- 所以没必要让缓存键无限膨胀。
换句话说,allowed_company_ids 是“值得缓存区分”的那一类上下文。
三、_eval_context() 里有两个很重要的公司变量
_eval_context() 会给规则域提供这些变量:
company_ids:当前激活公司的列表;company_id:当前主公司;user:带空上下文的用户对象。
这说明记录规则里的 domain 不是纯字符串,它其实是在一个受控的上下文里求值。
所以当规则里写了类似:
['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
它就会随着当前公司切换而变化。
四、_get_rules() 决定哪些规则参与计算
_get_rules() 先从数据库里把当前模型、当前 mode、当前用户组能命中的规则找出来。
接着 _compute_domain() 会把这些规则分成两类:
- 全局规则:没有 group 的,彼此是 AND;
- 组规则:有 group 的,彼此是 OR,然后再并回全局域。
这就是为什么“看起来只加了一条规则”,实际效果可能会非常严格:
- 全局规则会叠加;
- 组规则会按角色开放不同入口;
- 公司上下文又会再影响缓存结果。
五、调试时最该先看的不是“规则文本”,而是上下文
如果你在排查“为什么换公司后权限变了”,先别急着改 rule。
先看这些:
allowed_company_ids是什么;env.company是哪一个;- 规则里用的是
company_id还是company_ids; - 这条规则是 global 还是 group-bound;
- 当前是不是
sudo()状态。
很多多公司权限问题,其实不是规则写错,而是你在错误的上下文里读它。
结论
allowed_company_ids 会影响记录规则结果,不是因为 Odoo “记错了”,而是因为它明确把公司切换当作缓存和规则语义的一部分。
理解这一点之后,你会更容易判断:
- 这是缓存键变化带来的正常差异;
- 还是某条规则本身真的写错了。
DISCUSSION
评论区