POS 数据加载

Odoo POS 为什么刷新后还能“记得住门店数据”:IndexedDB、limited loading 与本地清理链路讲透

很多人把 POS 启动理解成“前端向后端拉一次主数据”,但 Odoo 真正在处理的是 load_data_params 动态字段关系、IndexedDB 本地缓存、按 last_server_date 增量加载,以及 filter_local_data 清掉失效记录。本文结合 point_of_sale 前后端源码讲清:为什么 POS 重载不一定全量拉、为什么本地旧数据不会永远残留,以及遇到刷新后数据怪异时该怎么排错。

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

很多人第一次看 Odoo POS,会把启动流程想得很简单:

打开前台,向后端拉一坨数据,页面就能用了。

这句话只对了一半。

真正的 Odoo POS 更像一个“带本地数据库的前端应用”:它不只会从服务器拿数据,还会缓存、增量更新、筛掉本地失效记录,并在字段关系变化时重建本地结构。

先说结论:Odoo POS 的启动与刷新,不是单纯 RPC 拉全量,而是“字段关系声明 + IndexedDB 缓存 + last_server_date 增量加载 + 本地无效数据清理”的组合拳。 如果你把它只当成一个普通网页,很容易误判很多“刷新后为什么还记得旧数据”或“为什么全量重载后问题又好了”的现象。

第一步:先拉的不是业务数据,而是“怎么读数据”

在前端 data_service.js 里,POS 启动前会先调用 pos.session.load_data_params()

这一步返回的不是订单、商品本身,而是每个模型的:

  • fields
  • relations

也就是说,前端先问服务器:

这次 POS 允许加载哪些模型、每个模型有哪些字段、字段之间怎么关联。

为什么这一步重要?因为 POS 前端用的是 related models 体系,本地对象之间不是扁平 JSON,而是有连接关系的。没有这份“数据结构说明书”,后面就没法可靠地把缓存数据恢复成可用对象图。

第二步:POS 真有自己的本地数据库

data_service.js 明确使用 IndexedDB 存服务器数据缓存。

这意味着:

  • POS 刷新页面时,不一定立刻丢掉所有主数据;
  • 即使网络暂时不好,也可能先从本地缓存恢复;
  • 某些模型会被当成 unique models 处理,直接覆盖;
  • 某些模型会走合并逻辑,而不是简单 replace。

所以“浏览器一刷新,为什么商品和配置还在”并不神秘,因为它本来就被设计成能从 IndexedDB 恢复。

loadInitialData() 真正在做什么

loadInitialData() 的思路大致是:

  1. 先从 IndexedDB 读取本地缓存;
  2. 根据 session 状态、当前 session id、是否从 backend 进入等条件,决定要不要重新从服务器取;
  3. 若要取服务器数据,则带上 pos_last_server_datepos_limited_loading
  4. 服务器按这些上下文决定是做增量加载还是更多数据返回;
  5. 最后把本地缓存和服务器新数据融合。

这就解释了一个常见现象:

  • 有时刷新非常快,因为大部分数据走了本地缓存;
  • 有时会明显慢很多,因为触发了更重的数据重载。

pos_last_server_date 在保护什么

pos.load.mixin 里有 _server_date_to_domain():当上下文里存在 pos_last_server_date 且启用了 pos_limited_loading 时,很多模型的 domain 会自动加上 write_date > last_server_date

这代表 Odoo 的增量加载不是“前端自己猜哪些改过”,而是:

  • 由服务器掌握最后同步时间;
  • 再按 write_date 缩小返回集。

注意一个很关键的边界:

  • pos.sessionpos.config 不走这层 limited loading 过滤;
  • 其他模型大多可以按改动时间做增量。

所以如果你发现配置级变化更容易触发重载,而商品、伙伴、税等模型更适合增量,这正是源码有意为之。

为什么配置变更后会触发“看起来像全量重建”的行为

loadInitialData() 会比较本地 pos.config._data_server_date 和后端 odoo.last_data_change

如果服务器记录的配置变动比本地缓存新,前端会:

  • reset IndexedDB;
  • 重新 init 本地结构;
  • 重新拉数据。

这很重要,因为字段结构或关键配置变了以后,继续相信旧缓存反而更危险。

所以全量 reload 不是浪费,而是在承认:

有些变化不是补几条 record 就能修好的,必须把本地缓存世界整体重建。

为什么本地旧数据不会永远赖着不走

很多人一看到本地缓存,就担心“那失效记录岂不是会永远留着”。Odoo 其实也考虑到了。

前端会把本地已有记录 id 送给 pos.session.filter_local_data();后端会检查:

  • 记录是否还存在;
  • 是否因 active=False 等原因已不再 relevant;
  • 某些取消订单是否应强制前端删除。

返回后,前端再把这些记录从 IndexedDB 以及内存数据里清掉。

这套链路的价值在于:POS 的缓存不是只会长胖,不会瘦身。

models.loadConnectedData() 在做什么

相关模型系统不是简单把 JSON 塞进 store。它会:

  • 按模型主键判断已有记录还是新记录;
  • 对已有记录做 sanitize 与 reconnect;
  • 恢复 UI state;
  • 在需要时触发 update 事件。

这解释了另一个常见现象:有些数据变化后,前端不是“整页白屏重来”,而是某些对象平滑地被更新。

为什么“Reload Data”有时能救命

pos_store.reloadData(fullReload) 会 reset IndexedDB;若用户选择 full reload,还会在 URL 上加 limited_loading=0,强制下一轮走更重的加载。

所以这个按钮之所以常能解决“数据怪异”,不是玄学,而是它在做两件很实的事:

  • 丢掉可能已不可信的本地缓存;
  • 让下一次加载不再过度依赖增量条件。

最容易误解的三件事

误区一:POS 刷新就是重新请求一次接口

不对。它先读缓存,再决定是否增量拉取,必要时还会重建 IndexedDB。

误区二:本地缓存意味着旧数据一定会越积越多

不对。filter_local_data() 就是专门拿来清理失效记录的。

误区三:全量 reload 只是“多等一会儿”

也不对。它常常意味着放弃一套可能已不可信的本地数据世界。

实战排错顺序

遇到“POS 刷新后数据不对 / 商品还在但配置不对 / 本地像卡着旧数据”时,建议按这个顺序查:

  1. 先看 load_data_params() 返回的字段与关系是否正确;
  2. 看 IndexedDB 里是否还留着旧缓存;
  3. pos_last_server_dateodoo.last_data_change 谁更新;
  4. 判断这次是否走了 limited loading;
  5. filter_local_data() 是否把失效记录正确清掉;
  6. 必要时执行 full reload,验证问题是否来自旧缓存世界。

最后的结论

Odoo POS 的“像 App 一样”体验,不只是 OWL 前端做得像,而是它真的带着一套本地数据层在运行。

所以当你看到它刷新后仍记得商品、客户、配置、草稿单,不要简单说“浏览器缓存了”。更准确的说法是:

Odoo POS 在用声明式字段关系、IndexedDB、增量同步和本地清理,把自己做成一个能离线容错、能增量更新的前端数据系统。

DISCUSSION

评论区

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