返回指南
Odoo 19.0 · 库存估值

为什么你的库存估值与会计核算不符以及如何解决(Odoo 19)

在永续(实时 / at invoicing)模式下,库存估值报告应与库存估值科目保持一致。两者出现偏差的原因是什么?本指南说明 Odoo 19 的默认配置、差异的实际来源、如何自查,以及现有方案的局限。

· 修订于 2026-07-19

定期(期末 / at closing)——会计分录在库存估值报告中手工建议生成。

永续(实时 / at invoicing)——系统在产品被开具账单或发票时自动生成估值分录。

以下内容针对 Odoo 19 在永续(实时)估值模式下、采用平均成本(AVCO)或先进先出(FIFO)的场景。(标准成本法的行为不同,每个单位承担相同的产品级成本,不在本指南范围内。)

1. 差异,以及被手工平账掩盖的问题

当财务发现库存估值报告的在手库存总额,与总账库存估值科目余额不一致时,常见做法是按差额补一张手工调整分录,账面平了,期间关闭,问题被视为已处理。

但被抹平的是一个净额。资产负债表上几千元的差异,可能是数笔明细相互抵消后的残值。

示例:两笔采购,货物均已收货并部分售出,供应商账单在发货之后才到并过账,一笔涨价、一笔降价。

采购单 实际发生 对库存估值科目的影响
PO1 收 200 件 @10,售出 150 件;账单在发货后按 @12 过账 已售 150 件的价差 150×2 滞留于科目:虚高 300
PO2 收 100 件 @20,售出 80 件;账单对账后按 @17 过账 已售 80 件的价差 80×3 滞留于科目:虚低 240
净额(报表上显示的差异)   60

两个方向相反的金额,被压缩成一个看起来无害的数字。报表上是 60,底下是 540。 而这两笔业务中没有任何人操作错误。

差异的绝对值大小,与底层问题的严重程度没有必然关系。 若原因始终未查清、下一期又以同样方式处理,调整金额会随之扩大。基于销售成本计算的毛利同样失去依据。

因汇率换算产生的几个单位的尾差属于正常。持续存在且不断扩大的差异不属于正常,它意味着某些交易改变了库存但未同步改变总账,或改变了总账却没有对应的库存移动。

2. Odoo 19 的默认配置

在排查原因之前,先明确系统的默认配置,这能大幅缩小排查范围。以下各项中,只有一项是系统强制的,其余均可修改。

  • 成本方法: 在一般设置与产品类别上设置,平均成本(AVCO)或先进先出(FIFO)。
  • 参与估值的产品范围: 产品类型为实物(Goods)且勾选追踪库存(Track Inventory)的产品参与估值。未勾选库存跟踪的实物、服务类产品与组合产品不出现在估值报告中。
  • 退货数量回写(默认开启): 退货向导中的 Update quantities on SO/PO 列默认勾选,确认退货即同步扣减来源单据上的收货或交付数量。该列默认隐藏在可选列控件之后。
  • 销项开票原则(默认为订单数量): 新建数据库中,类型为实物的产品,其开票原则默认为订单数量(Ordered quantities)而非已交付数量(Delivered quantities)。
  • 进项账单控制(默认为收货数量): 采购侧对应的是账单控制(Bill Control),实物商品默认为收货数量(Received quantities)
  • 锁定已确认单据(默认关闭): 销售与采购设置中的 Lock Confirmed Sales / Lock Confirmed Orders 默认关闭,确认后的单据默认仍可编辑。
  • 已完成的库存移动只读(系统强制): 状态为已完成的库存移动对所有用户只读且不可修改删除,驱动估值的移动记录保持固定。这是上述各项中唯一无法通过配置绕过的一条,Odoo 19 在数据源头做了强制保护。

机制补充

理解后续内容需要先明确一点:Odoo 取消了此前版本的收货暂估科目,在 V19 中,库存估值与总账是两本各自独立的账。

估值子账跟随库存移动实时计算,但从不写会计分录。总账只有三条入口:单据过账(供应商账单、客户发票)、库位估值科目(移动的某一端库位配置了估值科目时)、期末关账。除此之外,任何物理移动都不产生会计分录。

两侧的时点各不相同:

  • 进项侧: 收货时库存价值按订单价临时计入;账单过账时按账单价重估,并同时写入总账。
  • 销项侧: 发货完成时库存价值按当刻成本固定,此后不再改写;而总账的销售成本在开票时才写入。

小结。 配置本身不产生差异。当每一次实物移动都紧跟着对应单据过账时,「写总账的时点」与「固定库存价值的时点」贴合,估值报告与估值科目按确定性规则对账,其间的时点差可以自我解释。差异来自这两个时点被拉开的时候。

3. 差异的实际来源

持续性差异来自与系统机制时序不匹配的操作。以下几类在日常操作中都不显眼,其中多数不涉及任何违规动作。

成因一:开票原则设为「订单数量」时,开票与发货不同步

开票原则设为订单数量时,发票由订单数量驱动,可以先于发货开出,两个动作就此分开。

销售成本在开票时点结转,取的是开票当刻的成本;库存的实际减少发生在发货时点,取的是出库当刻的成本。两个时点之间只要该产品的成本变动过,两个数就对不上,差额永久留在存货科目,且不会自愈。

成本法会改变暴露方式,但不能消除风险。平均成本下,窗口内的任何一次收货都会重估成本,必然错位;先进先出下,只要发货时消耗的仍是开票时那一批,两者一致——但若该批在窗口内被其他订单先行消耗,或被补录的到岸成本、晚到的账单改了价格,同样错位。

结论:是否安全取决于开票与发货之间的窗口长度,而不取决于成本法。

成因二:改变库存但不经过单据的操作

盘点调整、报废,以及上线时以盘点方式导入的期初库存,都直接改变库存数量与价值,而这些库位默认不配置估值科目,因此不产生任何会计分录:库存报表变了,总账不动。

期初库存尤其值得注意——库存以盘点导入、总账另做一笔手工分录,两个数从一开始就未必真正对齐,此后每月的调整都在补这笔开账差。

若为这些库位配置估值科目,分录会产生,但仓库人员的一次盘点调整将直接写入损益表,没有审批环节。这个开关两端各有代价。

成因三:覆盖退货回写

Update quantities on SO/PO 列默认勾选且默认隐藏,取消勾选属于明确的人为操作:货物退回,但来源单据上的收货或交付数量保持不变。该单据仍然显示为已全部收货或全部交付,而对应库存已不存在,基于该数量做出的所有开票决策自此都是错误的。由于该列默认隐藏,事后复核交易时很难发现这一操作。

成因四:成本修正发生在货物发出之后

出库价值在发货完成时已确定不再改写。因此当修正后的账单价格或到岸成本在货物售出之后才录入时,仍在库的单位被正确重估,已发出的单位则拿不到新成本。在 AVCO 与 FIFO 下,系统不为此生成价差修正分录(价差科目是标准成本法专属),差价只能通过重估在库库存消化,而已发出的部分没有库存可供重估。

这笔差价最终停在哪里,取决于是以哪种方式录入的:

  • 修正供应商账单价格。 归属于已发出单位的差价留在存货科目中,直到期末关账将其扫入存货变动。
  • 补录到岸成本。 只有与在库数量相对应的那一部分被资本化进存货;其余部分不是等待关账的残值,而是留在到岸成本行所配置的费用科目里,直接成为当期损益。

两种情形下总额都不会错,错的是分类:本应计入这批货销售成本的金额被记到了别处,毛利随之失真。这类差异不会在余额上暴露,只会在毛利上暴露。第 1 节的示例即属于此类。

成因五:停留在撤销状态的账单

账单过账会重算库存成本层;把已过账的账单转为草稿、取消或删除,只撤销总账分录,不会反向重算成本层

如果紧接着把账单改正并重新过账,重估会再次触发,成本层按新账单的价格重算并覆盖旧值,总账也按新价重建分录,两本账重新对齐——这是正常的修正路径,撤销动作本身不是问题。

问题出在停留在撤销状态:总账分录已经撤销,成本层却仍停留在原账单估值之后的成本上。若此批货有售出,客户发票产生的销售成本贷方仍留在存货科目上,而对应的采购借方已经不在——存货科目因此出现贷方余额

用贷记单红冲同理:总账被冲平,成本层不参与重算。除非随后有一张正确的账单过账,否则成本层不会自行回到正确值,需要一次手工库存重估。

一类看似差异、实则不导致差异的情形

供应商账单价格与采购订单价格不一致时,AVCO 与 FIFO 将账单价格视为真实成本并据此重估该笔库存移动,总账与明细账同步更新,不产生背离。识别这一情形与识别真实差异同等重要——它正是在所有差异被合并为一笔调整时,掩盖真实敞口的那类自我消解的噪声。

除此之外,允许负库存、不同成本法行为,以及生产制造中的工时与折旧入成本等业务情况各有其机制,此处不一一枚举。

小结。 差异未必来自违规操作,多数来自总账时点与库存时点被拉开——数量上被拉开,或成本上被拉开。

4. 自查步骤

在委托任何分析之前,以下步骤可以确定问题的规模与方向。

  1. 确认架构。 在目标实例上收一次货,然后查看会计分录。零分录即确认当前为 Odoo 19 永续(实时 / at invoicing)架构,本文全部内容适用;若收货即产生分录,则前提不成立,需另行判断。
  2. 确定差额与方向。 在同一截止日期下,比较库存估值报告总额与库存估值科目余额,记录带符号的差额。方向与金额同等重要。
  3. 检查存货类科目有无贷方余额,以及长期停留在草稿或取消状态的采购账单。 资产类科目出现贷方余额通常指向后者(成因五)。
  4. 确认估值模式是否混用。 检查各产品类别的估值设置。若部分类别为定期、部分为永续,两者不能共用同一估值科目,也无法以同一方式对账。
  5. 剥离时点差。 在同一日期下量化已收货未开票、已交付未开票的金额。这属于正常的时间切分,不是差错。剥离之后剩余的部分才是真实差异。
  6. 复核锚点配置,并量化窗口。 检查高流转商品的销项开票原则与进项账单控制。销项侧保持订单数量,是最常见的结构性成因;进一步统计这些商品「开票早于发货」的订单行数量与平均间隔天数——窗口越长,成因一的敞口越大。
  7. 拆解到成因,验证代数和。 分别计算已收货未开票、已交付未开票、超额开票、被撤销或删除的已过账账单、开票与发货错时产生的成本发散,以及晚到账单在已售部分留下的价差,其代数和应当等于报表上的差异。对不上,说明还存在尚未识别的成因。

以上步骤可以确定差异的规模与大致位置。将其追溯至具体单据,需要针对企业自身的科目表与流程设计进行分析。

重要提示:结构性错误须在执行期末关账之前,以已过账口径的数量勾稽清理完毕。

5. 现有方案及其局限

针对估值差异的处理方式分为几层,均未封堵源头。

手工调整最常见。它为结账对平了总额,却未留下差异构成的任何记录,以净额掩盖了总额。

期末关账是许多企业实际依赖的机制,但性质需要澄清:关账是橡皮擦,不是校正器。 此举把估值与总账之间的一切背离不加区分地扫入存货变动。退货未开贷记单、开票数量不等于发货数量、撤销已过账账单、开票与发货错时的成本发散——这些结构性问题一旦被扫入,就被合法化为一笔费用,线索永久消失。总额确实平了,成因再也查不出来。

原生锁定功能Lock Confirmed Sales / Lock Confirmed Orders)默认关闭,开启后确认单据不可编辑。它约束的是「单据能不能被修改」,而非「时点能不能对齐」。即使全部开启,开票依然可以早于发货,退货取消回写依然可以操作,已过账的账单依然可以被转为草稿,后到的账单依然无法重估已发货部分。本文所列的差异成因,没有一类是「确认后有人改单」造成的。

侦测类工具——审计追踪、锁定日期、变更日志——事后记录操作人与操作内容。对追责确有价值,但不阻止交易发生,且覆盖不到差异真正产生的操作层:订单行、拣货、退货与对账。

纠正类工具——各类估值调整模块——列出报告与科目不符的产品,并生成分录强制科目与报告对齐。这类工具工程实现扎实,结账时确有用处,但处理的是症状。下一张早于发货开出的发票、下一次被取消勾选的退货回写,会重新打开缺口,模块下个月需要再运行一次。

几者共同缺失的是预防:在操作被执行的当下将其约束在机制允许的时序之内,并将敞口呈现在单据上,而不是在月末以汇总数的形式呈现。

6. SuiteState 的方案

SuiteState 自身在 Odoo 上经营批发业务,这个问题是从内部遇到的:库存估值科目与库存估值报告无法对平,经由收货、退货与滞后账单逐层追溯,最终归结为若干与机制时序不匹配的操作;而净差额远小于其背后任一方向的总敞口。

由此形成两项成果,两者在性质上有意区分。

诊断服务——一项只读分析,将既有差额拆解至各项成因——已收货未开票、已交付未开票、无贷记单的退货、已自我重估的价差——直至残差归零,且每一构成项都可追溯到具体单据。该分析基于客户自身环境构建,无法成为通用工具:拆解方式取决于客户的科目表、产品类别、流程设计以及会计管理重心所在。

预防产品——Data Guard 将仓库操作约束在机制允许的时序内,并在订单行上直接呈现已移动数量与已过账数量之间的带符号差额——为正表示超额开票,为负表示已移动但从未开票。敞口不再是报表上的汇总疑问,而成为结账清单上的具体事项,缺口在被封堵之后不会悄然重新打开。

如果你的库存价值与总账已经不再一致,第一步不是再补一张调整分录,而是查清这个差额究竟由什么构成。

《鹖冠子·世贤》记扁鹊论医:「长兄于病视神,未有形而除之。」 差异治理同理——在差额成形之前处理,与在报表上抹平它,是两件事。