先说结论
在 Odoo 里,invoice_policy 绝对不只是“开票时取哪个数量”这么简单。
它至少会影响:
qty_to_invoice怎样计算untaxed_amount_to_invoice怎样计算- 订单行何时显示
to invoice - 订单行何时显示
upselling - 服务、耗材、费用重计费分别如何得到
qty_delivered
所以很多项目里真正翻车的点,不是用户不会选“按订购”还是“按交付”,而是:
团队以为自己在切换一个发票偏好,实际上是在切换一整条销售兑现逻辑。
这篇文章主要参考哪些源码
核心参考文件包括:
/home/ubuntu/odoo-temp/addons/sale/models/product_template.py/home/ubuntu/odoo-temp/addons/sale/models/sale_order_line.py/home/ubuntu/odoo-temp/addons/sale/wizard/res_config_settings.py
最关键的源码信号有:
product.template.invoice_policy只有order与deliverysale.order.line._compute_qty_to_invoice()明确按 policy 走两套数量逻辑_compute_invoice_status()只有order场景才会出现upsellingqty_delivered_method在基础 sale 中会因 manual / analytic / stock / timesheet 等扩展而变化res.config.settings.default_invoice_policy只是产品默认值,不会回写历史订单
第一层:invoice policy 是产品属性,不是订单开票模式
很多实施会把开票策略讲成:
- 这张销售单按订购开票
- 那张销售单按交付开票
但源码里真正挂这个字段的,是 product.template.invoice_policy。
这意味着最先要搞清楚的一点是:
Odoo 默认不是“整张订单统一开票策略”,而是“每个产品自己决定这条销售行怎样进入待开票”。
所以一张订单里完全可能同时出现:
- 某些行按订购数量开票
- 某些行按交付数量开票
如果实施方脑子里只有“订单级开票模式”,后面看待开票数量时就特别容易误判。
第二层:qty_to_invoice 才是最核心的行为差异
sale.order.line._compute_qty_to_invoice() 里逻辑很直白:
- 若产品
invoice_policy == 'order' qty_to_invoice = product_uom_qty - qty_invoiced- 否则
qty_to_invoice = qty_delivered - qty_invoiced
这就是标准 Odoo 最本质的分叉点。
也就是说:
按订购开票
只要订单确认,系统就可以基于订购量认定“有多少该开票”。
按交付开票
系统先问的不是“卖了多少”,而是“已经被承认为交付了多少”。
这两个世界观完全不同。
前者强调商业承诺。 后者强调履约事实。
第三层:别把 qty_delivered 想成永远来自仓库出库
很多人一看到 delivered quantities,就自动联想到发货。
但基础 sale 里的 qty_delivered_method 已经说明,标准语义并不只属于库存:
- expense line 可能走
analytic - 服务 / 耗材在纯 sale 基础模块里可能是
manual - 安装
sale_stock后,耗材常会走 stock moves - 安装
sale_timesheet后,服务还可能走 timesheet
这就带来一个特别常见的误区:
- 顾问以为“按交付开票”就一定等着仓库出库单完成
其实不是。 对于不同产品类型与已安装模块,“交付”的事实来源可能完全不同。
所以实施里最该先确认的不是概念,而是:
- 这类产品的
qty_delivered_method到底是什么 - 谁在驱动这个数量增长
第四层:服务产品最容易在这里被误配
product.template 里对服务产品的提示写得很清楚:
invoice_policy == 'order'时,服务是“售出后即可按订购数量开票”invoice_policy == 'delivery'时,强调的是“基于交付数量,而不是订购数量”
这看起来像常识,但落地时最容易出问题。
因为很多团队卖的是:
- 固定包服务
- 预售工时包
- 后续按实际工时结算的咨询服务
- 先签单后逐步交付的实施服务
它们虽然都叫“服务”,但商业语义完全不一样。
适合 order
- 一次性顾问包
- 固定金额培训
- 签约即确认收入范围的服务包
更适合 delivery
- 按工时、按里程碑、按实际完成量结算
- 需要 delivered quantity 真实驱动开票的服务
如果你只因为“这是服务”就统一选一种 policy,后面不是提前开票,就是永远等不到待开票。
第五层:upselling 只出现在按订购开票,不是按交付开票
_compute_invoice_status() 里有一个非常容易被忽略的分支:
- 只有当产品
invoice_policy == 'order' - 且
qty_delivered > product_uom_qty - 才会把状态变成
upselling
这背后的意思很明确:
upselling 在这里不是“你多卖了”,而是“你多交付了,但合同按订购开票,系统提醒你有扩单机会”。
如果本来就是 delivery policy,那么多交付本身就会自然增加可开票数量,不需要靠 upselling 信号提醒。
所以很多人把 upsell 看成 CRM 意义上的销售机会,其实在 sale.order.line 里,它更像是:
- 一种订单兑现与合同数量不一致的提醒
- 而且只属于按订购开票的世界
第六层:default_invoice_policy 只是默认值,不会自动修正历史逻辑
res.config.settings 里的 default_invoice_policy 经常被误以为是“公司开票总开关”。
但它真正做的只是:
- 作为
product.template的默认值来源
这意味着它不会:
- 批量改掉历史产品的 invoice policy
- 回写已经生成的销售订单行逻辑
- 自动让旧单的
qty_to_invoice重走另一套规则
所以如果一家企业上线前把默认值设错了,后面再改设置,通常并不能消掉历史混乱。
真正要看的还是:
- 哪些产品已经是什么 policy
- 现有订单行在创建时拿到的是什么产品语义
第七层:费用重计费是“按交付开票”最容易被低估的分支
sale 基础模块里,expense line 的 qty_delivered_method 会被设成 analytic。
这说明对某些费用重计费场景来说:
- delivered quantity 不是人工填的
- 也不是出库单推的
- 而是从 analytic line 聚合出来的
这类场景最容易踩坑的地方在于,业务人员以为:
- 我卖的是一个服务产品,怎么“交付数量”还会跟费用、分析行、报销挂在一起?
但源码的答案是:
- 因为这里的“交付”表达的是可向客户主张的实际消耗事实,不一定非得是物流动作。
如果不了解这层,很多人会在报销重计费时错误地把产品改成 order policy,只为了让发票早点出来,最后把实际消耗与开票边界弄乱。
新手最容易误解的 6 件事
1. invoice policy 是订单级设置
不是,标准字段在产品上,订单行按产品语义运行。
2. 按交付开票就是等仓库发货
不一定,服务、费用、工时都可能有自己的 delivered quantity 计算方式。
3. 改了系统默认开票策略,历史单就会变正常
不会。默认值只影响以后创建的产品/记录。
4. 服务产品都该按订购开票
不对,很多按工时或按实际完成量结算的服务更适合 delivery。
5. upselling 跟 CRM 销售漏斗是同一个概念
不是。这里它主要是订单量与交付量错位后的提醒。
6. 按交付开票一定更“严谨”
也不一定。如果你的商业承诺本来就是签约即应开票,硬用 delivery 反而会造成运营摩擦。
实战里我建议怎么选
适合按订购数量开票
- 固定价格、固定范围、签约即确认收入边界
- 交付量不需要成为发票前置条件
- 更在意签约承诺,而不是逐次兑现量
适合按交付数量开票
- 实际完成量才是结算依据
- 工时、费用、出库、阶段性交付必须成为开票前提
- 需要用 delivered quantity 做业务控制
上线前一定要确认的 3 件事
- 产品的真实结算语义是什么
- 对应的
qty_delivered_method会由谁驱动 - 财务和销售看到的“应开票”是不是同一个业务口径
最后一句话
Odoo 的 invoice policy 设计,其实不是在问“发票喜欢按什么开”,而是在问:
客户的付款主张,到底来自签约承诺,还是来自被系统承认的实际交付。
把这个问题想清楚,order 和 delivery 就很好选。
想不清楚,再高级的自动化也会把待开票数量越算越乱。
DISCUSSION
评论区