先说结论
Odoo 的递延收入、递延费用,本质上不是“在发票上加两个日期然后均摊”。
它真正做的是:
把发票/账单行转成一条
account.asset记录,再由资产引擎生成后续确认分录。
所以你看到的是 deferred revenue / deferred expense,源码里跑的却是资产模型、折旧/摊销板、自动建资产、后续重算这一整套机制。
为什么很多人会低估这个功能
因为前台界面常让人感觉只是:
- 选个 model
- 填个 start date / end date
- 系统每月自动确认一点
但在 Odoo Enterprise 的 account_asset 里,递延收入和递延费用本来就和固定资产共用底层模型。
这意味着它天然带着:
- 资产状态机
- 期间边界计算
- future move 重算能力
- pause / modify / dispose 一类控制思路
所以它不是一个“发票附件式配置”,而是一种会计对象建模。
自动建递延对象到底发生在哪
enterprise/account_asset/models/account_move.py 里的 _auto_create_asset() 很关键。
发票过账后,Odoo 会检查行上的科目是否:
- 允许创建 asset
- 配了 asset model
- 金额非零
- 不是 tax line
- 还没生成过 asset
如果满足条件,就会创建 account.asset。
这一步对递延同样成立,只是这里的 asset_type 可能不是传统固定资产,而是:
- sale:递延收入
- expense:递延费用
也就是说,递延不是后续附加动作,而是过账时就把一条业务行提升成会计资产对象。
递延 model 为什么挂在科目上,而不只挂在产品上
源码里,account.account 与 asset_model_ids 有直接关系。
这很能说明 Odoo 的设计取向:
- 产品决定商业含义
- 科目决定会计归属
- 递延是否自动创建,最终更信任科目配置
所以实施时如果只盯产品模板,而忽略收入科目/费用科目上的 asset model 绑定,就很容易出现:
- 同类产品有的自动递延,有的没有
- 同样的发票行,因为改了会计科目,后续确认方式全变了
起始日期、结束日期、真正开始确认的日期,不一定是同一个概念
在 account_asset.py 里,你会看到:
acquisition_dateprorata_datemethod_numbermethod_periodprorata_computation_type
以及 _compute_prorata_date()、_get_last_day_asset()、_get_end_period_date() 这些逻辑。
这说明 Odoo 并不是简单拿“开始日期到结束日期”除一下。
它真正关心的是:
- 资产/递延对象从什么时候取得
- 第一个计算期间以哪个日期为 prorata 起点
- 按月还是按年确认
- 最后一个确认期间落在哪个 period end
所以用户口中的“开始摊销日期”,在源码里可能拆成多个语义层。
为什么递延收入和递延费用会沿用 depreciation board 思维
虽然业务上说的是 recognition,不是 depreciation,但计算引擎是一套。
compute_depreciation_board()、_recompute_board()、_compute_board_amount() 会统一负责:
- 清掉未来 draft move
- 按方法与期间重新生成 future move
- 已运行状态下把该过账的分录过账
对递延收入而言,这些 move 表达的是:
- 从负债逐步转收入
对递延费用而言,这些 move 表达的是:
- 从资产/预付逐步转费用
名称不同,底层仍然是“把总额按期间释放”的板式计算。
analytic distribution 会不会跟着走
会,而且这点特别容易被忽略。
_auto_create_asset() 创建资产时,会把原始 move line 的 analytic_distribution 带到 asset 上;
而 _prepare_move_for_asset_depreciation() 又会把 asset 的分析分摊带入后续确认分录。
这意味着:
递延对象不是只转金额,它还会尽量延续原业务行的分析归因。
如果你的收入/费用需要按项目、部门、成本中心持续确认,这个设计非常关键。
修改边界为什么比想象中严格
很多人想当然地认为递延对象创建后随时都能改日期、改金额、改节奏。
但 asset.modify wizard 明确限制:
- 锁账日前不能改
- 如果未来期间已经有 posted depreciation/recognition move,通常要先 reverse
- 修改后 future entries 会被重算
这代表 Odoo 的态度是:
- 已确认历史尽量保真
- 未来确认可以重算,但要显式走修改链路
所以递延不是 Excel 摊表,不能随便把过去与未来一起抹平重来。
实施里最常见的 3 类误区
1. 把递延当成发票字段增强
错。它本质是把业务行升级成 account.asset 对象。
2. 以为开始/结束日期直接等于每期确认边界
错。真正影响确认板的是 prorata、period end、method number / period 等内部规则。
3. 以为改原始发票行就会自动重写整条递延历史
错。历史确认与未来确认之间有明确边界,通常要靠 modify/recompute 流程处理。
建议的排错顺序
如果递延收入/费用结果不对,建议按这个顺序看:
- 原始发票/账单行的科目是否配置了正确 asset model
- 过账时
_auto_create_asset()有没有真的生成account.asset - asset type 是不是 sale / expense,账户映射是否正确
- acquisition / prorata / method period 是否符合你的会计政策
- 后续 recognition move 是没生成、没过账,还是被修改流程重算过
一句话记忆
Odoo 的 deferred revenue / deferred expense,本质不是“发票分摊”,而是“用资产引擎托管一段未来期间的收入或费用确认”。
DISCUSSION
评论区