先说结论
Odoo 的采购警告不是“谁写了提示,界面就原样弹出来”这么简单。
在 /home/ubuntu/odoo-temp/addons/purchase/models/purchase_order.py 和 account_invoice.py 里,这套机制至少包含三层语义:
- 先看当前用户有没有采购警告权限组
- 再把供应商本人、父级伙伴、产品行警告合并起来
- 最后以聚合文本形式展示在采购单或供应商账单上
所以现场里常见的“同一张单,我能看到警告,采购员却看不到”“明明供应商没写警告,为什么页面还是有提示”,往往都不是 bug,而是这三层逻辑叠加出来的结果。
源码主链路:不是单字段显示,而是先聚合再输出
采购单的核心在:
purchase.order._compute_purchase_warning_text()purchase.order.line.purchase_line_warn_msg
供应商账单侧还有一条对应链路:
account.move._compute_purchase_warning_text()
也就是说,Odoo 不是把一条 warning 字段直接塞到界面,而是先把多个来源整理成一段 purchase_warning_text。
这决定了它更像“面向采购决策的风险摘要”,而不是“某个字段的原样展示”。
第一层边界:没有权限组,系统直接当没这回事
源码第一句就很直接:
- 如果当前用户 不在
purchase.group_warning_purchase purchase_warning_text就直接置空
这意味着:
- 警告数据可能真实存在
- 但没有权限的用户,会像系统里根本没有警告一样
很多团队会误以为:
- 警告字段坏了
- 视图没继承好
- 数据没保存成功
其实第一步应该先查用户组。
看不见,不代表没有;经常只是没有被允许看。
第二层边界:系统不只看供应商本人,还会看父级伙伴
在采购单与账单的聚合逻辑里,Odoo 都会同时检查:
partner_id.purchase_warn_msgpartner_id.parent_id.purchase_warn_msg
这背后的业务假设很现实:
- 采购单上选中的,可能是某个联系人或分支机构
- 但风险提醒,往往维护在供应商主体公司上
所以你界面上选的是联系人 A,不代表系统只看联系人 A。
如果它的父公司有采购警告,照样会进聚合结果。
这就是为什么很多人会说:
我明明没给这个联系人写 warning,为什么采购单上还有?
答案通常是:父级伙伴有。
第三层边界:产品警告不是订单级字段,而是逐行收集后再拼起来
采购单不会只看供应商,还会遍历每一条订单行。
如果某个产品有 purchase_line_warn_msg,它就会把:
- 产品显示名
- 产品警告文本
一起加入聚合结果。
这意味着订单级警告和行级警告并不是互斥关系,而是会同时存在。
于是现场上就会出现一种非常典型的情况:
- 供应商没问题
- 但某个受限物料、替代料、试用品有采购风险提示
- 最终整张单依然出现一大段 warning
对业务来说,这反而更合理,因为采购决策本来就不只看“向谁买”,还要看“买什么”。
为什么它要用 OrderedSet,而不是简单字符串拼接
源码里用了 OrderedSet() 来收集警告。
这说明设计目标不只是“把内容都显示出来”,还包括:
- 去重
- 保留相对稳定的顺序
- 避免同一条提示反复出现
这点很重要。
如果一个产品在多行重复出现,或者供应商与父级伙伴存在重复文本,系统不希望把同一句风险说明刷满整个页面。
也就是说,Odoo 想表达的是:
让采购员知道有哪些风险点,而不是制造阅读噪音。
它为什么还会出现在 Vendor Bill 上
很多人以为采购警告只属于 RFQ/PO 阶段,但 account_invoice.py 里也做了同样的聚合。
这代表 Odoo 的设计不是“下单时提醒一次就完了”,而是把采购风险继续延续到:
- 供应商账单录入
- 对账与入账判断
这很有实际意义。
因为很多风险并不会在下单那一刻消失,例如:
- 该供应商需先核对资质
- 某产品需要补齐检验资料
- 某类账单需要人工复核
如果只在 PO 看得到,到 Bill 阶段反而容易遗漏。
最容易误解的 4 个点
1. 以为 warning 是“阻断逻辑”
默认这套机制更多是提示,不等于自动禁止确认或过账。
2. 以为只要 partner 没写警告,就不会有提示
父级伙伴和产品行同样会参与聚合。
3. 以为数据有了,所有人都看得到
权限组先行,没组就直接清空。
4. 以为这只是采购单界面的小功能
其实它已经延伸到 Vendor Bill,说明它是采购风险提示链的一部分。
实战排错顺序
如果你遇到“为什么这里有警告 / 为什么这里没有警告”,建议按这个顺序排:
- 先查用户是否具备
purchase.group_warning_purchase - 再查供应商本人
purchase_warn_msg - 再查父级伙伴
purchase_warn_msg - 逐行检查产品的
purchase_line_warn_msg - 确认是不是重复内容被 OrderedSet 去重了
- 如果是账单页,再确认 invoice line 是否已经正确关联到采购行/产品
这样排,比在视图里盲找字段快得多。
对实施和开发的提醒
如果你要做采购风控定制,不要急着改前端提示框。
更值得先想清楚的是:
- 你要把 warning 当提示,还是当阻断?
- 你要不要把公司级、品类级风险也纳入聚合?
- 你要不要让不同角色看不同层级的 warning?
因为 Odoo 现成这套实现,本质上已经是一个“轻量级采购风险汇总器”。
很多团队做二开失败,不是技术写不出来,而是一上来就把它简化成“某字段弹窗”。
一句话记忆法
Odoo 采购警告不是单点提示,而是按权限、伙伴层级和产品行聚合出来的一段采购风险摘要。
DISCUSSION
评论区