会计源码

Odoo Fiscal Position 为什么总让人误会“税和科目都会一起换完”:account mapping、tax mapping 与凭证分录边界讲透

站在发票界面看,Fiscal Position 像一个统一重映射器;站到源码里看,它其实分别作用在产品行、税行、payment term 行,而且作用时点也不同。本文把这种“像一起改、其实分层改”的边界讲清。

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

先说结论

很多人以为 Fiscal Position 一旦命中,就会把一张发票上的税和科目“整体替换”。

实际上 Odoo 的设计更像:

税映射和科目映射是两条并行但不完全重叠的规则链,它们分别作用在不同类型的行,并在不同阶段介入。

所以你会看到一种很典型的现象:

  • 产品收入/费用行换了
  • 税行跟着税映射变了
  • 但应收应付行不一定按同一逻辑改

这不是不一致,而是分录语义本来就分层。


为什么用户最容易在“整张发票”视角下误解它

在前台,Fiscal Position 往往只表现成一个头部字段。

于是人很容易形成一个错觉:

  • 这像一张“整单重映射开关”

但源码不是按“整单”处理的,而是按line type 处理的。

同一张发票里,至少有:

  • product/base lines
  • tax lines
  • payment term lines
  • 可能还有 rounding / discount 等辅助行

Fiscal Position 并不会对所有行做同一种映射。


tax mapping 主要改的是税链,而不是所有会计科目

在税计算链里,Fiscal Position 的 map_tax() 负责把原税替换成目标税。

这一步影响的是:

  • 发票行使用哪些税
  • 随后生成哪些 tax line
  • 税 repartition 进一步怎么拆会计行

所以 tax mapping 的真正影响面是:

改税对象,从而改后面整条税分录生成链。

它不直接等于“把所有 journal item 的 account_id 一把重写”。


account mapping 主要改的是基础业务行落账科目

account_move_line._compute_account_id() 里,产品行拿科目时,会调用产品账户解析,并把 fiscal position 传进去。

这意味着产品行的收入/费用科目选择,可能在:

  • 产品模板账户
  • 类别账户
  • fiscal position account mapping

共同作用下确定。

所以 account mapping 更像:

重定向 base line 应该记到哪个收入/费用科目。

它首先作用于业务行,而不是自动统治所有派生行。


payment term line 为什么又是另一套边界

payment_term 行的科目不是产品收入/费用科目,而是应收/应付结算科目。

_compute_account_id() 中,Odoo 会先根据单据类型选:

  • receivable
  • payable

再从 partner property、company fallback 等链路拿到账户,最后如果有 fiscal position,再对这个账户执行 map_account()

这很关键。

因为它说明:

  • payment term line 也可能被 fiscal position 映射
  • 但它的起点不是产品科目,而是伙伴结算科目

所以你看到“产品行和 payment term 行都变了”,不代表它们在走同一条 account selection 链。


为什么 tax line 也不是简单地“跟 account mapping 走”

税行首先来自 tax mapping 之后的税模型与 repartition 规则。

也就是说,tax line 的存在、金额、税务科目,本来就先受:

  • 哪个税被选中
  • 税 repartition 如何定义

支配。

所以在思维顺序上,应该理解成:

  1. Fiscal Position 先可能把税对象换掉
  2. 新税再通过 repartition 产出 tax line
  3. tax line 的科目来源是税配置,不是简单复用 base line account mapping

这也是为什么有人会困惑:

  • “我都映射了收入科目,税行为什么没跟着变成同一个科目?”

因为 tax line 本来就不该那样工作。


真正容易出错的是“顺序感”

很多实施问题并不是配置错,而是理解顺序错。

比较靠谱的顺序应该是:

  1. 先确定 fiscal position 是否命中
  2. 再看税是不是先被 map_tax() 替换
  3. 再看 product/base line 科目是否被 map_account() 改写
  4. 再看 payment term line 的 receivable/payable 科目有没有再被 map_account() 改写
  5. 最后再看 tax line 是由哪套税 repartition 产出的

如果你反过来把所有 journal item 当作一层统一映射,几乎一定越查越乱。


最常见的 4 个误区

1. 以为 fiscal position 只影响税

错。它也会影响某些 account mapping。

2. 以为 fiscal position 会平等影响所有分录行

错。不同 line type 的起点与规则链不同。

3. 以为 tax line 科目来自产品科目映射

错。税行更先受税与 repartition 配置支配。

4. 以为 payment term line 不会被 fiscal position 影响

也不对。它可能被映射,但走的是 receivable/payable 的选科目链。


一句话记忆

Fiscal Position 不是“整单统一换壳器”,而是分别在 base line、tax line、payment term line 上按不同时点和不同起点生效的分层映射器。

DISCUSSION

评论区

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