很多人第一次看到 Odoo POS 多设备协同时,会有一种错觉:
- 这边改了订单;
- 那边列表突然刷新;
- 客户屏也同步变化;
- 但又不是所有数据都靠 websocket 直接全量推过去。
于是常见的问题就来了:
- 为什么有时另一台设备会立刻刷新,有时又像慢半拍;
- 为什么 customer display 能跟着当前订单走;
- 为什么明明是一套配置,不同终端还能区分“是不是发给我”;
- 为什么同步通知来了以后,前端还要再去服务器读数据。
结论先说:Odoo POS 的多设备同步不是“服务器把所有新数据直接广播给所有终端”,而是“用 bus 做事件通知,用 access_token 分频道,再让前端按需二次拉取和合并数据”。
这套设计既解释了它为什么看起来“像实时”,也解释了为什么它并不是每次都完全即时、完全直推。
access_token 不是装饰字段,它是 POS 私有信道的一部分
在 pos.bus.mixin 里,模型会持有 access_token,创建时自动 _ensure_access_token()。发送通知时,_notify() 会把 token 作为 bus channel 的一部分。
这意味着 Odoo 并不是简单按“模型名 + 事件名”广播,而是通过 token 把消息送进更私有的频道。
对应到 POS 里,至少有两类对象非常依赖它:
- POS 配置 / session 级同步通知;
- customer display 这样的定向界面更新。
所以 access_token 的意义不是让 URL 漂亮,而是帮助系统建立“这条实时消息该进哪个门”。
notify_synchronisation() 发的不是完整世界,而是同步信号
在 pos.config.notify_synchronisation() 里,系统会:
- 收集静态记录;
- 发送
SYNCHRONISATION通知; - 把 session_id、device_identifier 等上下文一起带出去;
- 对 trusted configs 也发对应通知。
这里最重要的一点是:它发的虽然可能带一部分 static_records,但核心仍然是“提醒别人该同步了”,而不是“把所有动态状态一次性推完”。
这就是为什么前端收到通知后,DevicesSynchronisation.collect() 往往还会再执行 readDataFromServer(),去拉 open orders、删除记录和最新 write_date 对比结果。
为什么前端还要二次拉取
因为直接全量推送会遇到几个问题:
- 动态数据量可能很大;
- 终端本地还保有尚未同步的脏状态;
- 不同设备关心的数据子集不完全一样;
- 同一事件到来时,本地模型可能已经比通知里更“新”或更“旧”。
所以 Odoo 选择了一种更稳的模式:
先推“有变化”这个事实,再让客户端按自己上下文决定怎么补齐数据。
这比“服务器永远替你拼好最终状态”要更复杂,但也更适合 POS 这种可离线、可多端、可局部脏写的场景。
device_identifier 的作用:不是谁都该响应该通知
DevicesSynchronisation.collect() 收到同步事件后,会判断这是不是来自同设备。如果是同设备或同 session 某些不该处理的场景,就直接跳过。
这一步很关键。 否则你自己刚刚提交一张单,bus 又把同一通知回弹给自己,前端就可能陷入重复刷新、重复合并甚至覆盖本地状态。
所以 device_identifier 的存在,不只是区分机器编号,而是在多端协同里提供“谁该处理、谁该忽略”的边界。
customer display 为什么能跟当前订单一起跳动
customer display 有两条路径:
- 如果接了 IoT Box,会定期从本地硬件代理拉取;
- 否则就通过
BroadcastChannel与 bus 通知来更新。
在 customer_display_data_service.js 里,它会监听 UPDATE_CUSTOMER_DISPLAY-<device_uuid> 这样的定向事件,并把载荷合并进当前显示数据。
这意味着 customer display 不是“看整个 POS 世界”,而是看当前设备明确推给它的那一份订单视图。
这也是为什么顾客屏通常能紧跟当前收银动作,而不会乱串到别台终端的订单上。
最容易误解的三件事
误区一:POS 多端同步等于所有数据都实时 websocket 推全量
不对。 更准确地说,它是“事件推送 + 按需拉取 + 本地合并”。
误区二:customer display 只是一个被动镜像网页
不对。 它有自己的更新通道、目标 device 识别和数据合并逻辑。
误区三:收到同步通知就说明数据已经完全一致
也不对。 通知只说明“该同步了”,最终一致性还依赖后续 read / merge / delete 处理。
实战排错顺序
遇到“另一台不刷新 / 客户屏不跟单 / 同步看起来延迟”,建议按这个顺序查:
- 对应对象是否有有效
access_token; - bus 通知是否真的发出;
device_identifier是否导致当前终端把消息当作“自己发的”而忽略;- 前端
collect()后是否执行了readDataFromServer(); - 需要删除的旧记录是否被正确清理;
- customer display 当前走的是 IoT 路径还是 bus/BroadcastChannel 路径;
- 被推送的是否就是当前 device_uuid 的定向事件。
最后的结论
Odoo POS 的“多设备几乎实时”,真正靠的是三层协作:
- access_token:把消息送进对的私有信道;
- bus 通知:告诉相关终端“状态变了”;
- 前端二次拉取与合并:把每台设备自己的本地世界修正到最新可接受状态。
所以如果你把它理解成“纯推送实时数据库”,一定会困惑; 但如果你把它理解成“事件驱动的最终一致同步系统”,很多看似奇怪的刷新、延迟和定向更新就都说得通了。
DISCUSSION
评论区