当订阅里卖的是消耗品或实物,最大的难点不是“能不能出库”,而是每一期该算哪些 move、哪些已经开过票、下一个周期又该何时补货。
核心链路
sale_order_line.py的_get_stock_subscription_lines()先把“订阅 + consumable”挑出来,说明这套逻辑只针对会随账期反复履约的实物线。_get_outgoing_incoming_moves()与_get_incoming_outgoing_moves_filter()都按last_invoice_date/start_date/next_invoice_date划定期间窗口,只统计当前账期内的 move。企业版不是简单看累计已出库。_reset_subscription_qty_to_invoice()在每期开始时把qty_to_deliver重新拉回订阅行数量;而_reset_subscription_quantity_post_invoice()在开票后又会重新_action_launch_stock_rule(),为下一期准备补货动作。_prepare_procurement_values()还会把本期开票日期区间写进product_description_variants,让库存单据能带着 “2026-03-01 to 2026-03-31” 这类期间信息。stock_picking.py的_action_done()则专门抓“已经开过票但此后又确认的新 picking”,并通过_post_subscription_activity()给订阅单打告警。它提醒企业:实物流晚于账单,也必须被管理。
关键源码位置
/home/ubuntu/odoo-temp/enterprise/sale_subscription_stock/models/sale_order_line.py/home/ubuntu/odoo-temp/enterprise/sale_subscription_stock/models/stock_picking.py
容易误解的地方
- 误区一:订阅实物履约跟普通销售出库一样。这里最核心的是账期切片。
- 误区二:开票后库存逻辑就结束。企业版会为下一期重新发起 stock rule。
- 误区三:delivered qty 看累计历史即可。源码按本期开票窗口过滤 move。
实战注意事项
- 卖“月配一箱耗材”这类业务时,测试一定要跨两个账期,别只测首期开票。
- 若仓库抱怨拣货单说明不清楚,先看
product_description_variants是否已经带上期间。 - 发现“已开票后又出库”时,不要只补库存单,要查看系统生成的 subscription activity 是否已提示销售跟进。
结语
企业版这些代码共同说明一件事:真正可上线的业务流程,靠的不是“页面上看起来能点通”,而是权限、状态、时机、对账口径和跨模块回写都被收紧。理解这些边界,实施和二开时就不容易走进“功能演示能跑、真实业务一用就散”的坑。
DISCUSSION
评论区