先说结论
Odoo 采购权限不是一句“采购员建单、经理批单”就能讲完。
从源码看,至少有三层边界叠在一起:
- 你是不是采购用户,决定你能不能看到部分采购信息
- 你是不是采购经理,决定你能不能越过审批阈值
- 订单确认后是否锁单,决定你后续还能不能随便取消或修改
所以真正的责任划分更像:
可见性、审批权、锁单控制,是三套不同但串联的安全边界。
这篇为什么不是已有“双重审批”文章重复
站里已有文章已经讲过:
button_confirm()如何决定走purchase还是to approve_approval_allowed()如何看金额、币种和经理组
那篇重点在审批分流逻辑。
这篇换个角度,回答一个组织治理问题:
- 谁能看到采购相关信息
- 谁能代表组织承诺采购
- 谁要对确认后的锁定状态负责
也就是说,这篇讲的是职责边界,不是单一审批算法。
第一层:不是所有人都能看到同样的采购信息
在 addons/purchase/models/res_partner.py 里,purchase_order_count 的计算开头就先判断:
- 当前用户是否有
purchase.group_purchase_user
如果没有:
- 直接返回,不给采购统计
同一个文件里,purchase_order_count 字段本身也带了:
groups='purchase.group_purchase_user'
这说明采购访问不是只有“能不能打开采购菜单”那么简单。
连合作伙伴上的采购计数、采购统计这些信息,也要先过采购用户组。
这就是典型的可见性边界。
第二层:采购用户和采购经理不是一个安全等级
在 purchase.order._approval_allowed() 里,能直接通过审批的情况有三种:
- 公司配置为
one_step - 双重审批但金额低于阈值
- 当前用户属于
purchase.group_purchase_manager
第三条特别关键。
它说明经理组的意义不是“多看一个菜单”,而是:
- 在组织上拥有越过阈值直接批准采购承诺的权力
所以 purchase.group_purchase_manager 不是普通功能组,而是授权组。
这个边界如果配错,就会出现两类问题:
- 普通采购员本不该批的大额单被直接放行
- 真正负责的人却发现自己还要卡在
to approve
第三层:button_confirm() 和 button_approve() 其实分配了两种责任
在 purchase_order.py 里:
button_confirm()负责把单据推入审批分流button_approve()负责真正把单据写成purchase
并且 button_approve() 还会:
- 写入
date_approve - 在公司启用锁单时把
locked = True
这意味着:
- Confirm 更像“我认为这张单业务上成立,可以送审/送批”
- Approve 才像“组织正式承诺这笔采购”
如果你把两者混成一个动作,就很容易低估经理审批的责任。
第四层:锁单不是界面美化,而是确认后责任收口
源码里 button_approve() 会在 lock_confirmed_po == 'lock' 时自动锁单。
而 button_cancel() 又明确规定:
- 如果采购单已锁,不能直接取消
- 必须先解锁
同时,如果已有非草稿/非取消的供应商账单,也不能取消采购单。
这就说明锁单并不是“让页面看起来严肃一点”,而是:
- 限制确认后随意改单
- 把后续变更动作抬升成显式责任
- 防止采购承诺与账单事实脱节
所以锁单其实是权限治理的后半段。
前半段是“谁能批”,后半段是“批完后谁敢改”。
第五层:供应商信息回写还会绕开普通用户权限边界
在 _add_supplier_to_product() 里,源码最后用了:
product_tmpl_id.sudo().write(vals)
注释写得很明白:
- supplier info should be added regardless of the user access rights
这非常值得注意。
因为它说明 Odoo 在采购确认时,认为“把供应商补进产品供应商列表”是业务上应保证完成的动作,哪怕当前操作者不具备完整产品写权限。
这就形成了另一个边界:
- 用户权限限制的是日常直接编辑能力
- 但某些采购确认副作用会被系统以 sudo 方式落地
如果你不知道这点,就会在排错时误会:
- 为什么这个采购员明明没产品维护权,系统还是更新了 vendor 资料?
答案是:这是源码有意为之。
组织里最容易配错的 5 个点
1)把 purchase user 和 purchase manager 混发
这样会让审批边界虚化,大额单谁都能过。
2)只盯菜单权限,不看字段/统计可见性
采购统计、合作伙伴采购信息本身就受组限制。
3)把 Confirm 当成最终审批
在双重审批场景下,Confirm 只是把责任推到下一层。
4)启用锁单却没定义解锁责任人
最后就会出现“谁都不敢改、又谁都偷偷来问管理员”的混乱状态。
5)忽视 sudo 写 supplierinfo 的副作用
这会影响你对“谁改了供应商资料”的审计判断。
落地建议
如果你要把采购职责分得清楚,至少应该把规则写明:
- 采购用户:能建单、跟单、看采购统计到什么程度
- 采购经理:哪些金额/哪些公司可直接批
- 锁单后变更:谁能解锁,何时允许取消
- 主数据副作用:确认采购会不会自动补 vendor 信息,是否要记录审计
这样配置下来,Odoo 的采购安全边界才会和组织责任真正对齐。
一句话记忆
Odoo 采购权限不是单一审批开关,而是“采购可见性 + 经理审批权 + 确认后锁单控制”三层边界一起定义的责任系统。
DISCUSSION
评论区