循环盘点

Odoo 循环盘点为什么不是“给库位写个频率就完了”:cyclic inventory、next inventory date 与盘点排程边界讲透

很多团队把循环盘点理解成“库位每 30 天数一次”。但 Odoo 真正落地时,会同时看 location 的 cyclic inventory frequency、last inventory date、company 年度盘点日,以及 quant 自己的 inventory_date。本文把这套排程边界讲透。

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

很多仓库第一次启用 Odoo 循环盘点,都会把它想成一句很简单的话:

  • A 库位 7 天盘一次;
  • B 库位 30 天盘一次;
  • 系统到时间提醒一下,就结束了。

但源码里真正的设计并不是“单一频率字段驱动一切”,而是:

盘点计划先挂在 location 上,再落到 quant 上,并且还要和公司级年度盘点日期一起竞争。

这就是为什么同样写了库存频率,不同库位、不同公司、不同上次盘点时间,最后看到的 next_inventory_date 和 quant 的 inventory_date 可能完全不同。

一句话先讲透

Odoo 的循环盘点排程不是单点配置,而是三层合成:

  1. stock.location.cyclic_inventory_frequency 决定这个库位有没有周期性盘点节奏;
  2. last_inventory_date 决定下一次日期从哪一天开始往后推;
  3. stock.location._get_next_inventory_date() 还会把公司级年度盘点日一起纳入比较,最终把结果写到 quant 的 inventory_date

所以“什么时候该数这批货”,不是 quant 自己拍脑袋决定,而是 location 规则向下投影后的结果。

为什么 frequency 只写在 location,不直接写在 quant

因为 Odoo 在这里优先表达的是仓位管理策略,不是单件商品偏好。

仓库管理者真正想说的通常是:

  • 高价值小件区每周盘;
  • 大宗托盘区每月盘;
  • 中转区也要定期盘,但节奏不同。

这类规则天然属于 location,而不是单个 quant。

因此 stock.location 上既有:

  • cyclic_inventory_frequency
  • last_inventory_date
  • next_inventory_date

又有 _get_next_inventory_date() 这种“把库位规则向量化到 quant”的方法。Odoo 先让仓位说话,再让库存记录继承节奏,这很符合现场管理。

next_inventory_date 为什么不是简单的“last + frequency”

_compute_next_inventory_date() 的逻辑里有一个很关键的细节:

  • 如果还没盘过,就用今天加频率;
  • 如果盘过,就用 last_inventory_date + frequency
  • 但如果已经逾期,不会把下一次仍然留在过去,而是直接推到“明天”。

这层“逾期后推明天”的处理很重要。

它表达的不是数学精确,而是执行友好:

系统宁可把过期盘点重新排成一个可执行的近期任务,也不会继续保留一个永远落后的历史日期。

所以如果你看到某些库位的 next date 不是理论上的历史日期,不是算错,而是 Odoo 在主动把过期任务重新拉回执行窗口。

quant 的 inventory_date 为什么又是一层

很多人以为 location 已经有 next_inventory_date,quant 就不需要再存 inventory_date

其实 quant 这层非常必要,因为最后真正参与盘点动作的是“某个产品在某个位置、lot、package、owner 维度下的一条库存记录”。

stock.quant_compute_inventory_date() 里会读取 location 的 _get_next_inventory_date() 结果,把日期投到每一条 quant 上。这样后面做:

  • 物理盘点列表筛选;
  • 谁该先数;
  • 哪些记录已经过期;
  • 条码盘点端任务分发;

才有稳定抓手。

换句话说:

  • location 负责定义策略;
  • quant 负责承接待执行任务。

为什么公司级年度盘点日会“插手”循环盘点

_get_next_inventory_date() 里另一个很容易被忽略的点,是它会看 company 的:

  • annual_inventory_month
  • annual_inventory_day

如果公司配置了年度盘点日,location 自己算出来的 next_inventory_date 还要和公司盘点日取更早的那个。

这说明 Odoo 在设计上明确承认两类盘点制度并存:

  • 平时的循环盘点;
  • 财年/审计驱动的统一盘点。

而且统一盘点拥有很强的话语权。因为对财务与内控来说,全公司统一盘点窗口往往不能被局部仓位节奏覆盖。

last_count_date 解决的不是“谁最后改过数量”

很多团队看盘点历史时,只盯 quant 当前数量有没有变,却忽略了 last_count_date 的来源。

源码里它不是简单读 quant 自身更新时间,而是从 stock.move.line 里筛:

  • state = done
  • is_inventory = True

再按 product / lot / package / owner / location 这些维度去找最近一次盘点相关 move line。

也就是说,Odoo 认定“最近一次盘点时间”的依据,不是有人打开过页面,而是确实发生过盘点型库存调整。

这让循环盘点节奏能和真实执行闭环,而不是和表单编辑时间绑死。

实战里最容易踩的 4 个坑

1. 只配了 frequency,没理解 annual inventory date 会截胡

结果以为某库位应该 30 天后再盘,系统却更早把 quant 拉进年度盘点窗口。

2. 把 last_inventory_date 当成人工备注

它其实会直接影响下一轮排程,改错一天,整条节奏都会偏。

3. 以为 transit location 不参与循环盘点

源码明确允许 internaltransit 库位进入这套逻辑。中转位如果属于公司并配置了频率,也能被纳入计划。

4. 只看 quant,忽略 location 策略

quant 上看到的是结果,不是源头。排查“为什么今天该盘这批货”时,要先回到 location。

推荐的排查顺序

如果现场有人问:“这条库存为什么今天出现在盘点列表里?”

建议按这个顺序看:

  1. quant 的 location_id 是哪个库位;
  2. 该库位有没有 cyclic_inventory_frequency
  3. last_inventory_date 是哪天;
  4. 公司有没有年度盘点月 / 日;
  5. quant 的 inventory_datelast_count_date 是否吻合实际。

这个顺序比直接盯界面日期稳定得多。

最后的结论

Odoo 的循环盘点设计,核心不是“按天提醒”,而是把盘点节奏拆成:

  • 仓位级制度;
  • 公司级统一窗口;
  • quant 级待执行日期;
  • move line 级执行回写。

所以真正稳定的盘点体系,不是频率字段本身,而是你有没有把这四层边界看清。

DISCUSSION

评论区

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