现金制税

Odoo 现金制税为什么不是“付款后才补一笔税”:cash basis tax、partial reconcile 和税务转移账户到底怎么串起来

很多人把 Odoo 的 cash basis tax 理解成“发票不认税、收款时补一张税凭证”,但源码里的设计远比这更精细:它围绕 partial reconcile、付款比例、税务转移账户和多币种汇率共同运作。本文把这条链讲透。

Odoo 开发 会计
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

在 Odoo 里,cash basis tax 不是“付款之后再补一张税凭证”这么简单

它真正表达的是:

发票先把税额挂在过渡位置,等真实付款发生并形成核销后,再按“本次结清比例”把税额从 transition account 转到正式税务口径。

所以它的核心触发器不是“你点了收款”,而是:

  • 发票里确实有 tax_exigibility = 'on_payment' 的税
  • 发票上存在应收 / 应付 term line
  • 付款和发票之间真的形成了 account.partial.reconcile

这就是为什么 cash basis tax 本质上是跟着核销走,不是跟着按钮走。


源码在表达什么

/home/ubuntu/odoo-temp/addons/account/models/account_move.py_collect_tax_cash_basis_values() 可以看到,Odoo 会先把一张 move 中需要处理的行分成两类:

  • tax:本身就是 on payment 的税行
  • base:底层业务行带着 on payment 的税,需要后续按比例转税

/home/ubuntu/odoo-temp/addons/account/models/account_partial_reconcile.py_collect_tax_cash_basis_values() 则继续往下做第二层工作:

  • 识别这次 partial reconcile 对应的金额
  • 计算这次付款覆盖了原单多少比例
  • 判断要按哪个公司币还是外币来算比例
  • 推出本次 cash basis move 应该落多少税、多少 base

这说明官方不是“付款一次就全额转税”,而是:

每发生一次核销,就按这次核销覆盖的比例去搬动对应的税务金额。

所以 partial payment 才是 cash basis tax 的关键场景,而不是例外场景。


为什么一定要有 transition account

account.taxres.company 里都能看到 cash basis 相关配置,尤其是过渡账户与专门 journal。

这套设计是在解决一个现实问题:

  • 发票发生时,业务事实已经存在
  • 但税务上还不应当立刻进入正式申报口径

如果系统直接把税额打进正式税账户,后面就没法优雅地表达“税先记住了,但暂时还不 exigible”。

所以 Odoo 采用的是两段式:

  1. 发票先把税挂到 transition account
  2. 核销发生后,再通过 cash basis entry 把应转部分搬到正式税账户

这也是为什么配置不全时,源码会直接报错:没有 cash basis journal,系统根本不知道后续那张“税务转正”的凭证该落到哪里。


多币种为什么更容易让人看晕

源码里有一段很关键:如果发票和付款不是同一外币,或者汇率口径不同,系统会重新算 payment_rate

这说明 cash basis tax 不只是一个税问题,它还同时在处理:

  • 本次付款覆盖比例
  • 公司币金额
  • 外币金额
  • 付款日汇率

所以你会看到一个典型现象:

  • 发票时点汇率是一套
  • 付款时点汇率又是一套
  • 现金制税 move 与汇率差额 move 可能同时出现,但它们不是同一件事

一句话记:

cash basis 在决定“这次该转多少税”,exchange difference 在决定“同一笔业务因为汇率变化还差多少币值”。

别把这两张凭证混成一张。


新手最容易误解的 4 件事

1. 以为 cash basis tax 由发票过账触发

不准确。发票只是把后续可转税的基础准备好,真正触发通常发生在核销。

2. 以为一次付款就一定把税全转完

不对。部分收款 / 部分付款下,系统按比例转。

3. 以为 cash basis move 只看税行

不对。源码里明确区分 taxbase 两类 to-process lines。

4. 以为 cash basis 和多币种互不影响

错。源码直接把 payment_rate、公司币、外币一起纳入计算。


实战排查顺序

如果用户说“为什么现金制税没生成 / 生成金额不对”,建议按这个顺序查:

  1. 税的 tax_exigibility 是否真的是 on_payment
  2. 公司是否配置了 cash basis journal 与 transition account
  3. 发票上是否真的有 receivable / payable term line
  4. 付款和发票之间是否形成了 partial/full reconcile
  5. 是否存在多币种、部分付款或退款互抵场景

一句话记忆法

Odoo 的 cash basis tax 不是“付款后补税”,而是“在核销发生时,按本次结清比例,把税从过渡账户转进正式税务口径”。

DISCUSSION

评论区

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