寄售估值

Odoo 寄售库存为什么最怕“数量对了、价值错了”:owner stock、consignment 与估值排除边界讲透

很多团队已经知道 owner_id 会影响库存可用量,却没继续追到 stock_account:寄售货不仅是“谁能预留”的问题,更是“哪些 move、move line、quant 不该进本公司估值”的问题。本文把 owner stock 与 valuation boundary 讲透。

库存
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

很多人学 Odoo 寄售库存时,第一反应只有一层:

  • 货在我仓里;
  • 但 owner 不是我;
  • 所以预留时要看 owner。

这当然没错,但只说到一半。

真正容易出事故的地方不是“数量看不看得见”,而是:

这批货明明在公司仓位里,为什么估值、会计分录、库存价值报表却不该把它当成本公司的货。

如果这个边界没看清,就会出现最危险的情况:

  • 现场库存数量看起来正常;
  • 财务价值却被错误带进资产。

一句话先讲透

Odoo 对寄售 / owner stock 的处理,至少分两层:

  1. stock 模块里,owner_id / restrict_partner_id 决定这批货能不能被某次 move 合法占用;
  2. stock_account 模块里,只要 owner 不是公司 partner,相关 move、move line、quant 就会触发 _should_exclude_for_valuation(),从而被排除在估值之外。

所以寄售不是“库存里加个备注字段”,而是数量边界和价值边界同时变化。

为什么 owner stock 不能只停留在可用量层面

确实,在 stock.quant._get_available_quantity()_get_reserve_quantity() 这类底层方法里,owner_id 已经进入 gather domain。

这意味着:

  • 同一库位有货,不代表这次 move 就能随便取;
  • 只要 owner 条件不匹配,可用量就会被切开。

但如果你只停在这里,会漏掉更关键的一件事:

  • 即便数量在仓里,价值也未必属于你。

这就是 stock_account 继续接管的原因。

restrict_partner_idowner_id 为什么要分两层表达

很多实施顾问会问:

  • picking / move 上已经有 restrict_partner_id
  • move line / quant 上又有 owner_id
  • 为什么不统一成一个字段?

因为两者站位不同。

  • restrict_partner_id 更像 move 级需求约束:这张动作单希望只从谁的库存里取;
  • owner_id 更像实际库存事实:这条明细、这条 quant 当前归谁。

button_validate() 里,如果 picking 有 owner_id,系统会把这个约束同步到:

  • move 的 restrict_partner_id
  • move line 的 owner_id

这说明 Odoo 需要同时保留:

  • 上游业务要求;
  • 下游实际落地结果。

stock_account 的真正分界线:不是“在仓”,而是“是不是公司自己的 owner”

stock_account 里的 _should_exclude_for_valuation() 非常直接:

  • 如果 restrict_partner_id 存在,且不等于 company.partner_id,这个 move 应排除估值;
  • 如果 owner_id 存在,且不等于 company.partner_id,这个 move line / quant 也应排除估值。

这条规则极其关键,因为它说明 Odoo 的估值逻辑不是按“仓位是不是 internal”单独决定,而是还要再过一层所有权校验。

所以寄售货哪怕躺在内部库位里,也不自动等于公司资产。

为什么这是会计边界,不只是仓库边界

想象一个典型寄售场景:

  • 供应商货先放你仓里;
  • 你负责保管、拣货、发货;
  • 但在真正消耗 / 结算前,货权仍不属于你。

如果系统因为“货在 internal location”就直接把价值算进来,会导致:

  • 库存资产虚高;
  • 发货成本提前确认;
  • 对账时数量与价值口径不一致。

Odoo 正是通过 owner 相关估值排除,把“物理在场”和“财务归属”拆开。

quant 的 value 为什么可能是 0

很多人第一次在库存报表里看到某条 quant 明明有数量却没有价值,会怀疑是成本法、币种或 lot valuation 出了问题。

其实先别急着看成本法。

stock_account.models.stock_quant._compute_value() 里,如果 quant:

  • 库位不该估值;
  • owner 应被排除估值;
  • 数量本身为 0;

系统都会直接跳过。

也就是说,寄售 quant “有量无值” 往往不是 bug,而是正确表达。

move line 改 owner 为什么会牵动价值重算

stock_account 还把 owner_id 放进了 move line 的 valuation trigger 字段列表。

这意味着一旦 done 前后改动:

  • quantity
  • location_id
  • location_dest_id
  • owner_id
  • lot_id

系统就会尝试重算 move value。

这背后的业务含义是:

owner 不是展示字段,而是会改变“这部分数量是否参与公司库存价值”的核心条件。

所以随手修 owner,可能修的不只是仓库视图,而是财务口径。

实战最容易犯的 4 个错

1. 把 owner 当备注字段

结果数量分得开,估值却仍按公司库存算。

2. 只在 picking 上填 owner,没检查 move line / quant 是否正确落地

上游意图有了,下游事实没同步,后续追踪会断层。

3. 看到 internal location 就默认会估值

寄售是明显反例。internal 只说明货在内部空间,不说明价值归属。

4. 财务报表异常时只查 account move,不回头查 owner 边界

很多问题其实在库存层已经埋下了。

推荐的排查顺序

只要遇到“寄售货为什么数量有、价值没有”或反过来的问题,建议按这个顺序看:

  1. quant / move line 的 owner_id 是谁;
  2. move 的 restrict_partner_id 是谁;
  3. 公司 partner_id 是谁;
  4. 对应记录是否命中 _should_exclude_for_valuation()
  5. 再去看成本法、估值层与会计分录。

这样能先把边界判断做对,再看金额计算。

最后的结论

Odoo 对寄售库存最成熟的地方,不是“支持 owner 字段”,而是把 owner 真正接进了:

  • 可用量匹配;
  • 预留与拣货;
  • move / move line 约束;
  • quant 估值;
  • 会计边界。

所以记住一句话就够了:

寄售库存可以在你的仓里,但不一定在你的账里。

DISCUSSION

评论区

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