先说结论
Odoo 多网站定价不是“后台给网站配几张 pricelist,前台就照着显示”这么简单。
在 website_sale 里,真正发生的是一次 current pricelist 选路:
- 网站允许哪些 pricelist 先形成候选集;
- GeoIP 国家继续筛一层;
- 登录客户自己的 pricelist 可能插队;
- session 已选 pricelist 会被优先考虑;
- 当前购物车若已存在,还会进一步影响最终结果。
所以它的本质不是“网站默认价”,而是:
在网站、国家、客户、session、购物车这五种上下文之间求出当前价格语境。
一、为什么网站价目表只是候选集
网站配置里的 pricelist 很重要,但它只是起点。
真正决定排序和选路的,是 _get_pl_partner_order() 一类逻辑。
这意味着 Odoo 不是把网站价目表看成“最终真相”,而是:
- 一组允许出现在当前网站上的候选价格身份。
后面还要继续看:
- 当前访问者在哪个国家;
- 客户有没有自己的专属 pricelist;
- 当前 session 之前选过什么;
- 当前 cart 已经绑定了什么价格上下文。
二、为什么 GeoIP 在这里不是装饰能力
GeoIP 不是只用来切语言或者展示本地币种图标。
在 current pricelist 决策里,它会直接影响:
- 哪些国家组价目表优先进入候选;
- 哪些网站价目表应被排除;
- 当前 session 缓存的 pricelist 还能不能继续用。
也就是说,GeoIP 在 Odoo 网站定价里不是 UI 小增强,而是:
- 价格上下文的一部分。
三、为什么 partner pricelist 不能无条件压过网站规则
登录客户的 property_product_pricelist 确实会被考虑,但它不是天然凌驾于网站治理之上的超级答案。
源码会继续看它是否:
- 仍可用于当前网站;
- 仍匹配当前国家语境;
- 没有脱离当前可见 pricelist 池。
这很重要,因为多网站、多国家场景里,如果 partner pricelist 一登录就强行覆盖:
- 很容易出现“网站上看见了本不该看见的价格体系”。
四、为什么 session 缓存很重要,但不能盲信
_get_and_cache_current_pricelist() 会优先尝试读取 session 缓存。
但它不会直接盲用,而是还会重新验证:
- 这个 pricelist 还存在吗;
- 它还允许出现在当前网站吗;
- 现在的国家上下文下还合法吗。
这一步非常稳。
因为 session 最大的价值是:
- 避免每次请求都重算;
但 session 最大的风险也是:
- 用户环境变了,缓存还没变。
Odoo 这里采取的策略就是:
- 把缓存当提示,而不是当真相。
五、为什么购物车存在时,cart 的 pricelist 更接近事实
如果网站请求里已经有 cart,那么当前价格上下文就不该只靠“猜”。
因为 cart 本身已经承载了:
- 商品;
- 数量;
- 税务位置;
- 会继续往下游订单和付款链路走。
这时 cart 上的 pricelist 往往比 session 或 GeoIP 猜测更接近“当前真正生效的价格事实”。
所以 Odoo 会把 cart 也拉进 current pricelist 选路。
六、为什么这一套特别适合多国家多网站
因为它不是在问:
- “这个网站默认挂哪张价目表?”
而是在问:
- “对于这个访客、这个国家、这个网站、这个 session、这个购物车,现在最合理的价格身份是谁?”
这正是多国家、多客户分层、多站点定价体系真正需要的逻辑。
七、最容易误解的几个点
误解 1:网站价目表就是最终答案
不对。它只是候选池。
误解 2:GeoIP 只是界面增强
不对。它会直接影响 current pricelist 选路。
误解 3:partner pricelist 一定优先
不对。还要服从网站和国家边界。
误解 4:session 记住了就应该一直沿用
不对。缓存必须反复校验是否合法。
最后一句
理解 Odoo 多网站定价,重点不是背某个网站配置了什么,而是看懂这条主链:
网站可用 pricelist 候选集 → GeoIP 国家过滤 → partner / session / cart 共同约束 → current pricelist 缓存与回收。
看懂以后你就会知道,Odoo 在求的不是“默认价格”,而是“当前访问上下文下最合理的价格身份”。
DISCUSSION
评论区