POS 多设备同步

Odoo POS 多设备为什么能几乎同步刷新:access_token、bus 与 customer display 的联动机制讲透

很多人把 Odoo POS 当成一台浏览器里的本地前端,但它在多设备、客户屏与共享配置场景里其实大量依赖 bus 推送、access_token 信道与二次拉取。本文从 pos.bus.mixin、notify_synchronisation、DevicesSynchronisation 与 customer_display_data_service 讲清:为什么别的终端会突然刷新、客户屏为什么能跟着当前订单动、以及这套机制的边界在哪里。

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

很多人第一次看到 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() 里,系统会:

  1. 收集静态记录;
  2. 发送 SYNCHRONISATION 通知;
  3. 把 session_id、device_identifier 等上下文一起带出去;
  4. 对 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 处理。

实战排错顺序

遇到“另一台不刷新 / 客户屏不跟单 / 同步看起来延迟”,建议按这个顺序查:

  1. 对应对象是否有有效 access_token
  2. bus 通知是否真的发出;
  3. device_identifier 是否导致当前终端把消息当作“自己发的”而忽略;
  4. 前端 collect() 后是否执行了 readDataFromServer()
  5. 需要删除的旧记录是否被正确清理;
  6. customer display 当前走的是 IoT 路径还是 bus/BroadcastChannel 路径;
  7. 被推送的是否就是当前 device_uuid 的定向事件。

最后的结论

Odoo POS 的“多设备几乎实时”,真正靠的是三层协作:

  • access_token:把消息送进对的私有信道;
  • bus 通知:告诉相关终端“状态变了”;
  • 前端二次拉取与合并:把每台设备自己的本地世界修正到最新可接受状态。

所以如果你把它理解成“纯推送实时数据库”,一定会困惑; 但如果你把它理解成“事件驱动的最终一致同步系统”,很多看似奇怪的刷新、延迟和定向更新就都说得通了。

DISCUSSION

评论区

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