变更锁单

Odoo 采购单确认后为什么“能改、会锁、还能重发”:PO Amendment、Lock、Resend 与供应商确认边界讲透

很多人把 Odoo 采购单确认后的行为理解成两种极端:要么确认后彻底锁死,要么确认后想怎么改都行。源码里的真实答案更细:公司策略决定是否自动锁单,锁了以后取消前必须解锁;未锁时数量改动会留痕、已确认行不能随便删;而重发邮件和供应商 acknowledged 又是另一条协同链。

采购
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

Odoo 采购单确认后,并不是只有两种世界:

  • 完全不能改
  • 完全随便改

源码里的真实设计更像一套分层控制:

  1. 公司策略 决定确认后是否自动锁单
  2. 锁单状态 决定你能不能继续编辑、取消
  3. 已确认订单行 即便没锁,也不是想删就删
  4. 数量等关键字段变更 会在 chatter 留痕
  5. 重发邮件供应商 acknowledged 是协同确认链,不等于锁单

所以更准确地说:

Odoo 把“确认后修改”拆成了流程状态、锁单策略、字段留痕、供应商沟通四个层面,而不是一句“确认后可改/不可改”。


这篇为什么不是已有“receipt reminder / acknowledged”文章的重复

站里已有一篇重点讲:

  • 催交提醒什么时候发
  • acknowledged 为什么会影响提醒继续与否

那篇关注的是:

  • 供应商有没有确认收到采购单

这篇换成另一个现场更常问的问题:

  • 采购单确认后到底还能怎么改
  • 自动锁单和手工解锁的边界是什么
  • 为什么有时可以重发,有时又不该把重发当成“重新走审批”

也就是说,这篇主问题是 采购单确认后的生命周期管理,而不是提醒邮件节奏。


第一关键点:是否自动锁单,先看公司策略

/home/ubuntu/odoo-temp/addons/purchase/models/res_company.pyres_config_settings.py 里,采购公司策略里有:

  • po_lock
  • edit
  • lock

文案也很明确:

  • confirmed purchase orders are not editable

purchase.order.button_approve() 里,订单进入 purchase 后,如果公司策略是 lock,系统会写入:

  • locked = True

这说明“确认后能不能改”首先不是用户个人习惯,而是公司级采购治理策略。


第二关键点:锁单不是状态机主状态,但它会实实在在阻止后续动作

purchase.order 上有个独立字段:

  • locked

它不是 state 本身,但影响非常真实。

例如 button_cancel() 里,源码明确先检查:

  • 如果订单已锁,直接报错:必须先 unlock

也就是说,锁单不是视觉装饰,而是取消动作的前置门槛。

这很符合真实采购管理:

  • 已确认且锁定的采购单,不应该无痕撤销
  • 至少要先显式解除保护,再进入取消流程

所以 “locked” 可以理解成:

  • 对已确认采购单施加的额外编辑/撤销保护层

第三关键点:就算没锁,已确认采购单也不是任你改删

很多人看到公司策略是 edit,就会以为:

  • 那确认后就是完全自由编辑

也不对。

purchase_order_line.py 里,_unlink_except_purchase() 明确规定:

  • 如果订单行所在订单已在 purchase
  • 且这行不是 section/note 之类展示行
  • 就不能直接删除该采购行

这说明即便没锁,Odoo 也不希望你把已确认的真实业务行像草稿一样随手删掉。

它表达的不是“绝对不能改”,而是:

  • 确认后的修改要保留业务连续性,不应该把已承诺事实抹掉。

第四关键点:数量变更会留痕,不是静默覆盖

purchase_order_line.write() 里还有一个很有业务味的设计:

  • 如果订单已在 purchase
  • product_qty 发生变化
  • 系统会 message_post_with_source(...)

也就是说,确认后改数量不是纯字段更新,而是会在 chatter 里留下轨迹。

这解决的是采购现实里的一个关键问题:

  • 订单改过,但谁也不知道改了什么

Odoo 并没有把这种变更强行变成版本管理系统,但至少要求:

  • 数量类核心调整要有留痕

所以“PO amendment” 在标准 Odoo 里更像:

  • 有条件地继续编辑 + 留痕

而不是单独一个“修订单”对象。


第五关键点:新增行、改单价、改日期,还会继续影响后续库存与估值

purchase_stock 扩展里,已确认订单行的修改还会进一步影响:

  • move 的 deadline
  • 关联 picking 的更新
  • 已估值 move 的价值重算

比如:

  • date_planned 会更新相关 move 的截止日期
  • product_qty 可能触发 _create_or_update_picking()
  • price_unit 还可能影响 stock move 的价格与估值

这说明确认后的修改不是“界面补记一下备注”,而是会继续波及执行链路。

因此实施上更应该把“确认后允许修改”理解成:

  • 在受控前提下继续修正执行单

而不是:

  • 把确认状态当草稿继续用

第六关键点:重发邮件不是重新确认审批,而是重新发起沟通

action_rfq_send() 这条链也很容易被误读。

源码里它会根据状态选择:

  • 草稿/已发送时,展示为 RFQ 语义
  • 已确认后,展示为 Purchase Order 语义

并且 message_post() 在带 mark_rfq_as_sent 上下文时,会把草稿订单写成:

  • state = 'sent'

这说明“发邮件/重发邮件”主要解决的是:

  • 把采购单内容再发给供应商
  • 保持沟通状态与门户入口正确

它不是“重新走审批流”的按钮,也不是“重发后就自动取消旧版本”的版本机制。

所以现场里如果采购员说“我重发了一次给供应商”,更准确的系统理解是:

  • 同一张 PO 又发起了一轮邮件沟通

而不是:

  • 系统自动生成了新的修订版采购单

第七关键点:供应商 acknowledged 是协同确认,不是锁单确认

purchase.order 上的:

  • acknowledged

以及 action_acknowledge() 做的事非常简单:

  • self.acknowledged = True

这点特别值得强调。

它说明 acknowledged 的业务含义是:

  • 供应商确认已经收到采购单

它不等于:

  • 采购单进入不可编辑状态
  • 采购单完成法务签约
  • 采购单自动冻结所有字段

因此这几个概念不能混:

  • 确认(purchase state):内部采购流程通过
  • 锁单(locked):系统对已确认订单施加修改保护
  • 供应商确认(acknowledged):外部协同方确认已收到单据

三者相关,但不是同一个开关。


最容易误解的 5 个点

1. 以为确认后是否能改,只看订单状态

不够。还要看公司锁单策略与当前 locked 状态。

2. 以为没锁就等于完全自由修改

不对。已确认订单行仍有删除限制,关键字段变更也会留痕。

3. 以为重发邮件等于重新发起审批

它本质上是重新沟通,不是重建版本。

4. 以为供应商点了确认,采购单就等于锁死

acknowledged 只是外部确认收到单据。

5. 以为确认后改数量只影响界面显示

它还可能继续影响 picking、move deadline、库存估值等执行链路。


实战建议:确认后的变更怎么管更稳

如果团队经常要在确认后改 PO,建议先分清三类动作:

第一类:只是再次发送给供应商

  • 用 resend / send mail 思路理解
  • 重点是沟通,不是版本管理

第二类:需要微调数量、日期、价格

  • 确认公司是否允许不锁单或允许先解锁
  • 让采购员知道改动会留痕,并可能影响后续库存链

第三类:需要撤销或大改

  • 先看是否已锁
  • 再看是否已有 vendor bill
  • 再决定是解锁后取消、还是保留原单追加调整

这样比笼统说“确认后别改”更符合标准 Odoo 的真实边界。


一句话记忆法

Odoo 采购单确认后不是简单“能改或不能改”,而是由公司锁单策略、已确认行限制、变更留痕,以及邮件/acknowledged 协同链共同决定后续边界。

DISCUSSION

评论区

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