企业版审批

Odoo 企业版 Approvals 为什么不是“点同意/拒绝”而已:顺序审批、最低人数、必审人与活动队列边界讲透

很多人把 Odoo 企业版 Approvals 看成一个很轻的审批按钮系统。源码里真正重要的不是按钮,而是“最低审批人数 + 必审人 + 顺序审批 + mail.activity 队列”四层约束如何一起决定请求何时进入 approved、refused 或 waiting。

企业 协同办公
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

很多人第一次看 Odoo 企业版 Approvals,会以为它只是个统一审批壳:

  • 建一个请求;
  • 加几个人;
  • 别人点批准或拒绝;
  • 结束。

这只能解释界面,解释不了它为什么能承接企业里的真实审批规则。

enterprise/approvals/models/approval_request.pyapproval_approver.py 和测试一起看,你会发现官方真正设计的是一套 “状态不是由单个按钮决定,而是由审批拓扑决定” 的机制。

它至少同时处理四层东西:

  1. 最低审批人数:不是有人点了就算过;
  2. 必审人:有些人必须签,不可被人数规则替代;
  3. 顺序审批:后面的人可能根本还没轮到;
  4. 待办活动队列:谁当前该处理,不是前端猜的,而是 mail.activity 真正排出来的。

所以 Approvals 的本质不是“记录几个按钮结果”,而是“把审批门槛、审批顺序和待办责任同步到同一套状态机里”。


一、真正的核心不是 approver 列表,而是 request + approver 双层状态

请求本体 approval.request 有一个总状态:

  • new
  • pending
  • approved
  • refused
  • cancel

但每个审批人 approval.approver 也有自己的状态:

  • new
  • pending
  • waiting
  • approved
  • refused
  • cancel

这两个状态不是重复的。

request_status 回答:整单现在处于什么阶段

也就是业务上:

  • 还没提交;
  • 已提交流转中;
  • 已通过;
  • 已拒绝;
  • 已取消。

approver.status 回答:这个审批人在流程里的位置

也就是:

  • 还没开始;
  • 轮到你处理;
  • 还在排队;
  • 你已通过;
  • 你已拒绝;
  • 这个请求已取消。

因此一个请求能是 pending,但当前用户看到的自己状态可能是 waiting

这正是“流程状态”和“个人位置状态”的区别。


二、为什么最低审批人数不是“凑够票数”这么简单

approval_minimum 是第一层门槛。

action_confirm() 在提交时会先检查:

  • 当前 approver 数量是否至少达到最小值;
  • 不够就直接报错,不让进入待审批。

但更关键的是后面的 _compute_request_status() 语义:

  • 并不是某一个审批人点了 approved,整单就过;
  • 要看累计已批准人数是否达到最小值;
  • 同时还得看必审人是否都满足。

测试里也明确覆盖了:

  • approval_minimum = 1 时,一个人过就可能整体通过;
  • approval_minimum = 2 时,先过一个仍然只是 pending

这说明官方把审批门槛理解成:

请求是否通过,由“全局通过条件”决定,而不是由“最近那个点击动作”决定。


三、为什么 required approver 比 minimum 更硬

approval.approver.required=True 是第二层门槛。

这是很多人最容易误判的地方。

有时会直觉认为:

  • 最低审批人数是 1;
  • 已经有 1 个人通过;
  • 那就应该通过。

但测试明确证明:

  • 如果那个必审人还没批,整单仍然不能过;
  • 即使你已经“凑够人数”,也只是 pending

这说明 Odoo 把 required 理解成一种 结构性约束

  • 它不是“额外加一票”;
  • 它是“这条链上必须经过这个节点”。

所以真正的通过条件不是:

  • approved_count >= minimum

而是更接近:

  • approved_count >= minimum
  • 所有 required approver 已批准

required approver 不是数量规则的一部分,而是数量规则之外的强制拓扑。


四、顺序审批为什么要引入 waiting

如果 category 开启 approver_sequence,审批人不是一起生效,而是按顺序推进。

action_confirm() 里会做这件事:

  • 当前第一个可处理 approver → pending
  • 后续 approver → waiting

这说明系统不是简单把所有人一起丢到待处理队列,而是显式表达:

  • 谁现在该审;
  • 谁只是未来候选。

为什么不能都给 pending?

因为一旦都给 pending,会立刻带来两个问题:

  1. 责任混乱:到底现在谁该动?
  2. 顺序失真:后面的审批人可能抢先批准,破坏顺序规则。

所以 action_approve() 里还有一个保护:

  • 如果当前用户状态是 waiting,直接抛错,不能越级审批。

waiting 不是 UI 灰色标签,而是防止越序审批的业务锁。


五、为什么审批流要和 mail.activity 绑在一起

Approvals 不只改状态,还会调 _create_activity() 给当前 approver 建待办。

这点很关键。

很多轻审批系统把“待审提醒”当成附属通知。 但 Odoo 把它做成流程主体的一部分:

  • 当前该审的人变成 pending
  • 同时收到对应 activity;
  • 如果拒绝/取消/切回草稿,相关活动也会被清理或反馈完成。

这意味着什么?

意味着审批不是“状态存在数据库里,用户自己想起来去看”。

而是:

  • 状态 决定当前责任;
  • activity 把责任派到人头上。

Approvals 真正联动的是状态机 + 待办队列,而不是状态机 + 邮件提示。

这也是它更像企业流程工具的原因。


六、为什么“撤回”不是简单改回 pending/new

action_withdraw() 做的不是一键回到初始,而是:

  • 把后续 approver 重新打回 waiting
  • 当前 approver 写回 pending
  • 同时配合取消/重建活动链。

这表示 Odoo 把 withdraw 理解成:

从当前审批断点往回收,而不是重置整单历史。

这很重要,因为在企业审批里:

  • 有时只是某个审批人撤回自己的批准;
  • 并不代表前面发生过的节点都要被抹掉。

所以它不是“清空所有记录”,而是“按顺序回退流程位置”。


七、为什么拒绝会向后传播

action_refuse() 之后,_update_next_approvers() 会把后续相关 approver 也更新成 refused,并取消活动。

这说明官方的判断是:

  • 一旦链路上某个关键节点拒绝;
  • 后面未执行的审批也没有继续意义。

因此拒绝不是局部事件,而是终止事件

这和批准不一样:

  • 批准只会推进到下一个;
  • 拒绝会把后续等待链一起压死。

这就是审批流里“正向推进”和“反向终止”的不对称性。


八、为什么确认时要校验经理、附件、最少人数

action_confirm() 一次性检查了很多前置条件:

  • 若 category 要求经理审批,则 request owner 的员工档案必须有 manager,且 manager 还必须有 user;
  • 若要求文档,必须至少有一个附件;
  • 若 approver 不足最小人数,不让提交流转。

这说明 Odoo 把 Approvals 看成:

  • 不是先放进流程再慢慢补资料;
  • 而是提交瞬间就应满足进入流转的基本合法性。

confirm 在这里不是“保存”,而是“准入校验 + 流程启动”。


九、为什么 approved 请求不能删,只能 archive

源码明确限制:

  • 已批准请求不能删除,只能归档。

这是很典型的企业审计边界。

审批通过后,记录已经不再只是草稿内容,而是:

  • 一个发生过的业务决策;
  • 可能关联附件、产品行、消息、活动反馈。

如果允许随手删,审批系统就失去最基本的可追踪性。

所以官方选择的是:

  • draft/pending 可以更自由处理;
  • approved 进入“保留痕迹”模式。

十、实战里最容易误解的 4 个点

误区 1:审批通过只看批准人数

不对。

还要看必审人是否满足。

误区 2:顺序审批只是前端显示顺序

不是。

waiting 会阻止后面的审批人抢先通过。

误区 3:activity 只是提醒,不影响流程

也不对。

activity 是当前责任链的落地形式。

误区 4:withdraw 就是全单回到草稿

不是。

它更像从当前断点往回收缩流程位置。


总结

approvals 源码串起来以后,你会发现 Odoo 企业版做的并不是一个“同意/拒绝按钮集合”。

它真正做的是一套审批拓扑控制:

  • approval_minimum 约束通过门槛;
  • required approver 保证关键节点不可跳过;
  • waiting / pending 表达顺序审批位置;
  • mail.activity 把当前责任真正派发出去;
  • withdraw / refuse / cancel 控制流程回退与终止;
  • archive 代替 delete 保留已批准决策痕迹。

所以 Odoo 企业版 Approvals 的本质,不是“审批按钮”,而是“把审批门槛、顺序和责任队列压进同一套状态机”。

这才是它值得学的地方。


参考源码 - enterprise/approvals/models/approval_request.py - enterprise/approvals/models/approval_approver.py - enterprise/approvals/tests/test_approvals.py

DISCUSSION

评论区

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