退货链路

Odoo 采购退货别只看退货单:qty_received、to_refund 与 Vendor Refund 的联动

从 purchase_stock 的 qty_received 计算、stock return picking 和账务回滚,解释退货为什么会同时影响收货数量、待开票数量和成本。

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

先说结论

采购退货不是一条“库存反向回滚”这么简单的链路。

它至少会同时影响三件事:

  • 库存:退回多少货
  • 采购行qty_received 怎么算
  • 会计:是否应该再开 vendor refund / credit note

所以你看到的“退货单”只是表面。真正复杂的地方,是 Odoo 要判断这笔退货到底算不算“已经收过、又退回来”,以及是否要让后续账务跟着反映。


源码里,qty_received 不是简单求和

purchase_stock/models/purchase_order_line.py 里,_prepare_qty_received() 不是把所有相关 move 的数量直接加起来,而是先区分:

  • 普通入库 move
  • 采购退货 move
  • dropship 相关的特殊退货
  • 是否已经绑定 origin_returned_move_id
  • 是否要走 to_refund

核心逻辑很清楚:

  • 采购退货如果满足条件,会从 qty_received 里扣掉
  • 某些特殊退货不会重复扣两次
  • 有些场景只退库存,不一定立刻把采购收货量再减一次

也就是说,qty_received 是一个业务解释后的结果,不是“所有 move 数量的机械总和”。


to_refund 的意思,比“退货”更关键

很多人看到退货就默认“应该退款”。其实 Odoo 还要看 to_refund

在退货场景里:

  • 有退货:说明货物回去了
  • to_refund:说明这笔退货应该影响供应商账单

如果只有库存层面的退货,没有 to_refund,那它未必会变成供应商退款逻辑。

这就是为什么同样是“退货”,有时只是仓库动作,有时却会触发后续的 vendor refund 流程。


退货单创建时,Odoo 会把采购链带回来

purchase_stock/models/stock.py 里,stock.return.picking 被扩展了。

当退货的地点是 supplier 时,Odoo 会尝试把:

  • purchase_line_id
  • partner_id

从原始链路里重新找回来。

这样做的目的很直接:

退货不是一张孤立的库存单,它要知道自己属于哪张采购单。

只有把采购链找回来,后续的数量回写、账务联动、责任人追踪才不会断。


stock.move 里,退货还能反向影响采购单

purchase_stock/models/stock_move.py 中,_action_synch_order() 会在某些已完成 move 上,反向创建或绑定采购行。

这里有两个关键点:

  1. 不是所有 move 都会同步到 PO
  2. 如果是 supplier / transit 方向的退货,数量会被视为负向

这说明采购与库存之间不是单向写入,而是会根据 move 的方向、状态和来源,决定要不要回写到采购单。


会计层的联动:不只是“退了货”,还要看“账开没开”

purchase/models/account_invoice.pypurchase_stock/models/account_invoice.py 里,Odoo 还会把采购单、供应商账单和退款单串起来。

典型逻辑包括:

  • 从采购单或旧 bill 反向自动补全 invoice
  • 匹配 in_invoice / in_refund
  • 根据已开票与已收货差异,计算后续应不应该提醒用户申请 refund
  • 在 valuation 里,把 refund 的金额和数量一起算进去

所以,采购退货真正关心的不是“仓库里少了一箱货”这么简单,而是:

这箱货少了以后,采购收货、待开票数量、成本和供应商退款是否还一致。


最常见的误解

误解 1:退货 = qty_received 一定直接归零

不对。它会根据 move 类型、origin_returned_move_id 和 to_refund 分别处理。

误解 2:退货单一定等于 vendor refund

也不对。库存退货和账务退款是两层逻辑。

误解 3:只要把库存退掉,采购链就自动完美回滚

也不对。Odoo 还要同步采购行、发票行和成本链路。


实战建议

如果你在排查采购退货问题,按这个顺序看:

  1. 退货 move 的方向是不是 supplier
  2. 有没有 origin_returned_move_id
  3. to_refund 是否开启
  4. 采购行的 qty_receivedqty_to_invoice 是否一致
  5. 是否已经有关联 bill / refund

把这五步看完,基本就能知道问题卡在哪一层。


结尾

采购退货的本质不是“倒着走一遍入库”,而是同时重算库存、采购和会计三条链

理解这一点,qty_receivedto_refund、vendor refund 这些看似零散的字段,就会变成一条完整的业务故事。

DISCUSSION

评论区

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