很多人第一次看 Dropship 采购时,会直觉地想:
“既然都是买货,为什么不能统一合到一张 RFQ 里?”
Odoo 的答案很直接:不能只看商品,还要看这单货最终会送到哪里、由谁追踪、走哪条路线。
Dropship 场景里,采购不是单纯“买回来入库”,而是把货直接送到客户或指定地址。正因为最终落点不一样,Odoo 才必须把合单边界卡得很严。
一、_run_buy() 不是在“凑单”,而是在按规则分组
在 addons/purchase_stock/models/stock_rule.py 里,_run_buy() 会先为每个 procurement 找到供应商,再用 _make_po_get_domain() 构造采购单域。
这个域就是合单边界。
只要公司、供应商、地址、路线、日期或其他关键条件不同,domain 就会不同。domain 不同,Odoo 就不会把两组需求硬塞进同一张 RFQ。
这其实很合理:
- 同一个供应商不代表同一张采购单;
- 同一个产品不代表同一路径;
- 同样是 buy 规则,也不代表同一个履约终点。
二、Dropship 的目的地本身就是关键差异
在 purchase_order.py 里,_get_destination_location() 和 _get_final_location_record() 专门处理 dropship。
普通收货会落到仓库库存位;但 dropship 会把目标直接指向客户地址或对应的外部目的地。
这意味着:
- 你看起来像是在“买同一种货”;
- 但库存语义上其实是两条不同的物流路径。
路径不同,就不能随便合单。因为一旦合单,后续的收货、发货、追踪和对账都会混在一起。
三、reference_ids 让 Odoo 记住“这单货从哪来、为谁服务”
Dropship 场景里,追踪信息比普通补货更重要。
_prepare_picking() 会把 reference_ids 写进采购相关对象里,后续再通过 Command.set(...) 之类的方式保留下来。
这不是装饰字段,而是很实用的业务锚点:
- 它能把采购单和上游来源关联起来;
- 它能帮助 Odoo 判断哪些需求可以合并;
- 它也能让后续排查“这张单为什么被拆开”时有据可查。
如果 reference 不同,哪怕产品相同,系统也可能故意不合并,因为那已经不是同一个业务上下文了。
四、路线与供应商要同时成立,buy 才真能跑起来
_filter_warehouse_routes() 会在有 buy 规则时检查路线是否真正可用;_run_buy() 里也会先找匹配供应商,再决定是否继续。
这说明一个重要原则:
Dropship 采购不是“看到需求就建单”,而是“路线、地址、供应商、追踪信息都对得上,才允许合并或生成”。
所以当你看到系统没有把两条需求合并,不要第一反应就怀疑 bug。先看:
- 收货地址是不是一致;
- 路线是不是一致;
- 供应商是不是一致;
- reference_ids 有没有不同;
- company 和 warehouse 有没有不同。
五、实战排查时最值得先看的 5 个点
- 目的地址:是不是同一个 dropship 目标?
- 采购域:
_make_po_get_domain()生成的 domain 是否完全一致? - 供应商:是不是同一个 seller / partner?
- reference_ids:是否有不同来源单据?
- 公司和仓库:跨公司或跨仓库时,Odoo 通常会更保守。
这 5 个点里,只要有一个不一致,Odoo 把它拆成不同 RFQ,往往就是对的。
结论
Dropship 采购之所以不会随便合单,不是因为系统“太保守”,而是因为它在保护真实业务边界:
- 货最终送到哪里;
- 这单货属于谁;
- 追踪链路要怎么保留。
理解了 _make_po_get_domain()、_get_destination_location() 和 reference_ids 这三件事,你就能很快判断 Odoo 为什么合、为什么拆、以及什么时候该手动介入。
DISCUSSION
评论区