先说结论
Odoo 的 storage category,不是“给库位写个最大容量”这么简单。
更准确地说,它是在回答一个更完整的问题:
这个库位允许放什么、能放多少、按什么维度计算容量,以及新货进入时能不能和现有货混放。
所以它不是单纯的“上限值”,而是一套库位接纳规则。
这也是为什么你只用“最大件数”去理解 storage category,往往会越配越乱。
为什么很多人第一次会配错
因为从业务语言上看,大家最容易想到的只有一句话:
- 这个货架最多放 100 件
但仓库现实里真正会卡住的,往往不是一个简单数字,而是:
- 重量有没有超
- 同一库位能不能混不同产品
- 同种产品最多允许放多少
- 某种包裹类型最多允许堆几个
- 库位不是空的情况下,能不能再塞新货
Odoo 在 storage category 里,其实是在把这些限制结构化。
所以它的设计重点不是“记录一个容量”,而是:
把库位约束从“经验口头规则”变成系统可判断的规则对象。
源码抓手:addons/stock/models/stock_storage_category.py
核心模型主要有两个:
stock.storage.categorystock.storage.category.capacity
只要看懂这两个模型,你就能明白它不是一个平面配置表,而是分了几层:
- 类别本身的总规则
- 具体容量规则条目
- 按产品与按包裹类型的不同容量语义
第一层:storage category 是一组“接纳原则”
在 stock.storage.category 上,最值得注意的字段有:
max_weightcapacity_idsallow_new_productlocation_idscompany_id
这里的结构已经说明了很多东西。
max_weight
不是数量上限,而是重量上限。
capacity_ids
说明容量规则不是只有一种,可能是一组细项。
allow_new_product
这特别关键,它不是在问“最多放多少”,而是在问:
- 空位时才能放新产品?
- 只有现有货和新货相同才能放?
- 还是允许混放?
这已经不是容量计数,而是混放策略了。
所以 storage category 的真实角色更像:
库位准入政策 + 容量政策的组合。
第二层:为什么产品容量和包裹容量要分开
源码里 capacity_ids 会通过 _compute_storage_capacity_ids 分拆成:
product_capacity_idspackage_capacity_ids
这设计非常值得注意。
因为仓库里“容量”不是只有一种度量法。
按产品容量理解
更像:
- 这个库位最多放某产品多少件
- 某个 SKU 在这个区域只能容纳到什么程度
按包裹类型容量理解
更像:
- 这个货位最多能塞几个托盘 / 周转箱 / 某类包装单元
这两个问题在业务上完全不是一回事。
如果混成一个字段,系统就很难表达真实仓储规则。
所以 Odoo 才会把两类 capacity 分开,但底层仍归在同一组 capacity_ids 里统一管理。
allow_new_product 才是很多项目里最容易被低估的字段
它有三个值:
empty:库位为空时才允许新产品进入same:只有现有产品和新产品相同时才允许mixed:允许混放
这说明 Odoo 并没有把库位容量理解成“只要没超量就能塞”。
它还在判断:
这个库位在语义上是否允许引入新的产品组合。
这很重要,因为很多仓库问题不是“放不下”,而是“原则上不该混”。
例如:
- 同一货位只允许一个 SKU
- 空位才能重新分配给新产品
- 混放会影响盘点与拣货效率
如果你忽略这个字段,只盯着数量上限,最后系统经常会表现得“明明还有空间,为什么不能放”。
其实它卡的是准入策略,不是空间本身。
第三层:stock.storage.category.capacity 不是备注表,而是精确规则表
这个模型里最关键的字段有:
storage_category_idproduct_idpackage_type_idquantity
这意味着每一条 capacity 规则,都是在回答:
- 针对某个 storage category
- 对某个产品,或某个 package type
- 最多允许多少数量
重点是“产品”和“包裹类型”通常二选一,而不是随便都填。
这让容量规则从一句模糊的话,变成了真正可判断的数据项。
约束设计也很说明问题:Odoo 不允许你把规则配成一团糊
源码里有几条很有代表性的约束:
max_weight >= 0
重量上限不能是负数。
quantity > 0
容量条目必须是正数。
同一 storage category 下,product_id 唯一
不能给同一个产品配置多条冲突容量规则。
同一 storage category 下,package_type_id 唯一
不能给同一个包裹类型重复配置容量。
这几条看似简单,其实是在保护系统可解释性。
否则你很容易出现:
- 同一产品两条不同容量规则到底听谁的?
- 同一包裹类型一会儿最多 4 个、一会儿最多 6 个
所以 Odoo 的意思很明确:
容量规则可以丰富,但不能暧昧。
为什么它要同时支持 product 和 package type
因为真实仓库里,有两种常见管理粒度:
粒度 1:按货品本身
适合散货、零件、箱内件数管控。
粒度 2:按包装单元
适合托盘位、整箱位、笼车位、周转箱位。
如果你的仓库是“按托盘位管理”,那真正该限制的常常不是产品件数,而是:
- 这个货位最多几个托盘
- 这种托盘能不能进这个货位
这就是为什么 storage category 很适合和 package 场景一起理解,而不是只拿它去套传统件数仓。
它解决的其实不是“库存多少”,而是“库位能不能接货”
这一点很关键。
stock.quant 更像回答:
- 现在这里有多少货
而 storage category 更像回答:
- 这类货 / 这类包,按当前规则,允不允许继续放进来
所以它偏向仓位治理,不是库存余额展示。
这也是为什么很多项目上线后才发现:
- 数量逻辑没错
- 但上架行为总不符合现场预期
根因往往不是 quant 问题,而是库位接纳策略没设计清楚。
实战里最常见的 4 个误区
1)把 storage category 当成“最大容量数字”
这样会忽略重量、混放策略、包裹类型等关键维度。
2)只按产品数量设计,不考虑包裹单位
结果托盘位、整箱位、笼车位等场景会很别扭。
3)忽略 allow_new_product
然后发现系统“不让放”,却以为是容量算法坏了。
4)同一种业务规则想用多条重复 capacity 硬拼
这通常会和唯一约束打架,也说明你的规则粒度设计不干净。
项目里更实用的落地方式
如果你要设计 storage category,我建议先别急着配字段,先问现场 3 个问题:
- 这个库位限制的是重量、件数,还是包装单元数?
- 这个库位允许混放吗?允许到什么程度?
- 现场是按 SKU 管理,还是按 托盘 / 箱 / 包裹类型 管理?
把这 3 个问题答清楚,再去配:
max_weight- product capacity
- package capacity
allow_new_product
会顺得多。
小结
官方源码把 storage category 设计成今天这个样子,其实很有代表性:
它不想只做一个“库位最多放多少”的小字段,而是想把仓位规则表达清楚。
所以最实用的一句话总结是:
Odoo 的 storage category,不是容量数字,而是库位接纳策略。容量只是这套策略里的一个维度。
一旦你这样理解,很多上架、混放、包裹位设计问题都会顺很多。
DISCUSSION
评论区