POS 前台经常给人一种错觉:
- 选个客户,价格变了;
- 切个税制,税额变了;
- 单据能下去,就说明定价没问题。
但在 Odoo 里,POS 定价从来不是“显示层改个数字”这么简单,它同时牵动价目表权限、币种合法性、税映射和含税价修正。 所以很多门店碰到的不是“为什么没变价”,而是“为什么看起来变了,但后台会计口径又不对”。
先说结论:Odoo POS 的价格与税不是两套独立系统,而是一条链:先选合法 pricelist,再映射 fiscal position,最后按税包含关系重新修正 price_unit。 任何一步理解错了,前台和后台就会开始“各自都像对的”。
第一道门:不是所有价目表都能在 POS 用
在 models/pos_config.py 里,Odoo 明确限制了 POS 可用价目表:
- 默认价目表必须包含在
available_pricelist_ids中; - 可用价目表必须与 POS/公司币种一致;
- 价目表公司必须属于当前公司或无公司。
这意味着:客户档案上挂了一个 pricelist,不代表 POS 前台一定能用它。 如果该价目表不在门店配置白名单里,POS 还是会退回默认价目表。
所以“客户选了没变价”,第一反应不该是前端 bug,而应该先查门店配置是不是允许这张价目表进入收银场景。
第二道门:客户价目表并不天然优先于门店默认价目表
pos.order 里有个关键逻辑:如果订单绑定了客户,订单可把 partner.property_product_pricelist 带进来;但初始化时,如果没有可用客户价目表,系统会回落到 session 对应的 config.pricelist_id。
这就带来一个很现实的边界:
客户价目表只有在“存在 + 合法 + 已加载到 POS”这三个条件都成立时,才会真正覆盖门店默认价。
所以实施现场常见的“客户在销售模块是 VIP 价,POS 还是门店零售价”,并不神秘,通常就是白名单没放进来,或者 session 打开时没有把新规则带进去。
fiscal position 不是备注字段,而是真正改税账户语义的映射器
在 pos_order.py 里,订单有 fiscal_position_id,订单行则会计算 tax_ids_after_fiscal_position。源码先用 fpos.map_tax(self.tax_ids) 做税映射,再把映射后的税参与 compute_all()。这说明 fiscal position 在 POS 里不是“给报表打标签”,而是实打实决定本单该按哪套税算。
这对以下场景尤其关键:
- 堂食 / 外带不同税率;
- 本地客户 / 跨境客户不同税制;
- B2C 零售与免税销售共存。
如果你只改了税本身,没理 fiscal position,POS 可能显示出你想要的价格,但发票、分录和税报表仍会跑偏。
为什么切税制以后,price_unit 还会再被修正一次
这是最容易被忽略的一层。
在订单行 onchange 逻辑里,Odoo 会先用价目表拿基础价格,再用 fiscal position 映射税,最后调用 _fix_tax_included_price_company() 来修正 price_unit。原因很简单:当原价是含税价,而映射后税集变了,不能只换税名,不换单价。
举个常见例子:
- 商品目录价是含 13% 税;
- 某类客户在 POS 应按 0% 或另一税率处理;
- 如果不修正单价,前台总额和后台税额会出现结构性错位。
因此很多人以为 Odoo “换税后偷偷改价”,其实不是偷偷改,而是在保护含税价逻辑不要被硬生生掰断。
preset 也会参与定价与税制,不只是客户会影响订单
源码里 pos.preset 也能带 pricelist_id 和 fiscal_position_id。这意味着在餐饮、自提、预约等场景里,订单模式本身就可能决定价目表与税制,不一定要等到选客户才切换。
这类门店如果前台逻辑没梳理清楚,就很容易出现:
- 操作员以为自己切的是“配送/自提模式”;
- 系统实际上顺手切了价目表和税制;
- 最后问题表现成价格突然变了、税额也跟着变。
最容易误判的三个现场现象
误区一:前台显示价正确,就说明后台也正确
不对。POS 前台只是结果展示,真正的税分解和会计落地还要看 fiscal position 映射后的税集。
误区二:客户价没生效,一定是客户卡片没保存
不对。更常见的是这张价目表不在 POS 可用集合里,或者币种/公司不合法。
误区三:切税制不该影响单价
也不对。只要你用的是含税价体系,税映射变化就有可能触发单价修正。
排错顺序,建议别跳步
遇到 POS 价格/税额异常,建议按这个顺序查:
- 先看 POS config:
use_pricelist是否启用,默认价目表是否包含在 available list; - 再看币种与公司: 价目表、invoice journal、门店币种是否一致;
- 再看客户与 preset: 是客户切了价,还是 preset 把价和税一起切了;
- 再看 fiscal position: 是否正确映射了税,堂食/外带是否用了不同税制;
- 最后看单价重算: 映射后税为含税/未税切换时,price_unit 是否被修正。
这个顺序很重要,因为很多团队一开始只盯着“屏幕上的价签”,却忽略了 POS 真正难的地方是:同一个价格,可能同时受到价目表和税制度两侧约束。
真正该设计的,是“谁有权改价、谁有权改税”
从源码看,Odoo 已经把价格与税映射做成了系统化链路。实施时真正要花力气的,其实不是教店员点哪里,而是提前定义清楚:
- 哪些价目表允许进 POS;
- 哪些客户能触发专属价;
- 哪些营业模式用不同 fiscal position;
- 价格是含税展示还是未税展示;
- 出问题时,先由运营查配置,还是先由财务看税映射。
因为在 POS 场景里,价格问题如果只看“卖多少钱”,往往看不出真问题;只有把 pricelist、fiscal position 和税包含关系放在一条链上,你才会真正看懂 Odoo 为什么“只是切了个客户,整张单的价格结构都变了”。
DISCUSSION
评论区