电商库存

Odoo 电商库存为什么不是“有货/没货”二元判断:free_qty、cart_qty 与可下单边界讲透

很多电商库存提示看起来只是前台小文案,但 website_sale_stock 实际把仓库维度、购物车占用、是否允许缺货下单和支付前再校验串成了一条完整边界。

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

先说结论

Odoo 电商库存判断,并不是一句简单的“仓库里还有多少”。

website_sale_stock 真正关心的是三件事:

  1. 当前网站应该按哪个仓库看库存;
  2. 当前购物车里这个商品已经占了多少数量;
  3. 这个商品是否允许缺货继续下单。

所以前台看到的“还剩 3 件”“库存不足”“允许继续购买”,背后并不是同一条规则在换皮,而是:

free_qtycart_qty、网站仓库和缺货策略共同决定的前台库存边界。

这就是为什么很多实施里“明明库存还有,前台却说不够”或者“列表页能买,结账页又被拦”,其实都不是 bug,而是不同层级的库存语义在起作用。


一、为什么 Odoo 网站库存先看 website warehouse

website_sale_stocksale.order 上重写了 _compute_warehouse_id()_get_shop_warehouse_id()

核心思路很清楚:

  • 网站订单优先使用网站自己的仓库;
  • 如果网站没配置仓库,再回退默认逻辑。

这说明 Odoo 的网站库存不是“公司全局库存”的直接投影,而是:

  • 以网站视角理解库存。

这在多仓、多站点场景特别关键。

因为业务真实问题不是:

  • 系统里总共有多少货;

而是:

  • 这个网站面向的履约仓,现在还能卖多少。

二、为什么前台库存不是只看 free_qty

很多人只盯着 product.free_qty,但源码里更重要的是:

  • _get_cart_and_free_qty(product)
  • _get_cart_qty(product_id)
  • _get_free_qty(product)

Odoo 会把两类数量一起算:

  • 仓库维度的可用库存 free_qty
  • 当前购物车里已经放进去的数量 cart_qty

这点特别容易被忽略。

因为如果你只看 free_qty,一个用户可以在同一购物车里反复加购,前台每次都觉得“库存还够”,最后总数却超了。

Odoo 明确避免这个问题:

库存边界不是“单次加多少”,而是“加完之后整张 cart 一共会占多少”。


三、为什么 _verify_updated_quantity() 是真正的库存闸门

website_sale_stock/models/sale_order.py 里,最关键的方法就是 _verify_updated_quantity()

当商品是可库存商品且 allow_out_of_stock_order=False 时,系统会:

  1. 取出当前 cart 里该商品数量;
  2. 取出对应网站仓库下的 free_qty
  3. 把数量统一换算到当前请求 UoM;
  4. 计算这次改动后的 total_cart_qty
  5. 如果超出,就返回允许的最大数量和 warning。

这条链路说明 Odoo 对库存限制的理解不是“超了就抛错结束”,而是更偏用户友好:

  • 能留多少就留多少;
  • 不能留时明确告诉你为什么;
  • 必要时直接把 line 降量甚至移除。

这也是为什么前台有时不是报硬错误,而是自动把数量调回可接受范围。


四、为什么商品页提示和支付前校验不是同一层

website_sale_stock 同时改了:

  • 商品组合信息返回值;
  • 前台 JS 的 availability message;
  • 支付前 _check_cart_is_ready_to_be_paid()

这说明 Odoo 故意做了两层边界:

第一层:前台提示层

商品页、变体切换、数量输入框会根据:

  • free_qty
  • cart_qty
  • show_availability
  • allow_out_of_stock_order

去渲染提醒、badge、最大可输数量。

第二层:支付阻断层

真正去付款前,还会再检查一遍各行 _check_availability(),如果仍有问题,就在 _check_cart_is_ready_to_be_paid() 抛出校验异常。

这很合理,因为前台提示层更多是用户体验;支付前校验层才是最后业务边界。

所以“页面能加购物车,但付款时被拦住”并不一定矛盾,而是:

  • 前台提示是尽早提醒;
  • 支付校验是最后兜底。

五、为什么“允许缺货下单”会彻底改变库存语义

allow_out_of_stock_order 是这套机制里最该被认真理解的开关。

一旦它为真:

  • 很多限制逻辑会直接放松;
  • _is_sold_out() 的语义会改变;
  • 前台库存文案也会偏向继续下单而不是强拦截。

这不是一个普通展示选项,而是商业策略开关。

它决定你的网站到底是:

  • 库存严控型电商;
  • 还是允许预售/延期履约型电商。

实施时最容易出错的地方,就是业务方口头上说“缺货也能接单”,但页面、支付、提醒、库存邮件却还按“现货严控”思维在跑。


六、为什么购物车里的库存感知比商品列表更重要

源码里不少逻辑都优先围绕 request.cart 做文章,而不是只盯商品卡片。

这非常合理。

因为网站库存真正危险的地方,不是用户看到某商品页面时,而是:

  • 他已经把多个商品、多个数量、多个变体一起放入同一 cart 之后。

也就是说,电商库存管理最重要的不是“商品当前能否展示可买按钮”,而是:

  • 这张购物车现在还能不能成立。

所以 Odoo 会反复回到 cart 维度去重算,而不是只在商品页做一次展示判断。


七、最容易误解的 5 件事

1. 以为网站库存就是公司总库存

不对。网站可以绑定自己的仓库视角。

2. 以为前台限制只看 free_qty

不对。源码还会扣掉当前 cart 已有数量。

3. 以为页面提示和支付校验是同一层逻辑

不对。一个偏体验,一个偏最终边界。

4. 以为“允许缺货下单”只是把按钮放开

不对。它会改变整套库存语义。

5. 以为库存问题一定是商品页问题

不对。真正关键的是整张购物车在当前仓库视角下是否还能成立。


八、实战排错顺序

电商库存提示不一致时,我会按这个顺序看:

  1. 网站绑定的是哪个仓库;
  2. 商品是否 allow_out_of_stock_order
  3. 当前 cart 里该商品已经有多少;
  4. UoM 换算后数量是否被 round down;
  5. 支付前 _check_cart_is_ready_to_be_paid() 是否仍发现某行不可用。

这样查,通常比只在前台盯着“库存数字怎么显示”更快找到根因。


一句话记忆

Odoo 电商库存不是“仓里还有多少”的单点判断,而是“网站仓库下的 free_qty,减去当前 cart 已占数量,再结合缺货策略后形成的一整套可下单边界”。

DISCUSSION

评论区

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