POS 收银员切换

Odoo POS 切换收银员为什么不只是“点一下头像换个人”:minimal 角色、价格权限与经理兜底边界讲透

很多人以为 POS 切换 cashier 只是前台把当前操作人换一下名字,但 Odoo 真正处理的是“这个人切进来之后能不能改价、能不能打折、能不能现金存取、关店差异要不要经理兜底,以及浏览器会把谁记成当前收银员”。本文结合 point_of_sale 前端源码,讲清 cashier 切换背后的真实权限边界。

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

很多门店会把 Odoo POS 的切换收银员理解成一个很轻的动作:

  • A 下班,
  • B 接手,
  • 前台点一下头像,
  • 当前订单继续卖。

这个理解只看到了表面。

源码里真正关键的问题其实是:切过去以后,这个人在前台到底还能做什么,不能做什么,哪些动作必须让经理兜底。

所以更准确的说法是:

Odoo POS 的 cashier 切换不是“换显示名”,而是把一组前台操作边界切到另一个人身上。

先说结论

结合 point_of_sale/static/src/app/services/pos_store.jsLoginScreenProductScreenclosing_popup.js,可以先抓住四个结论:

  1. 当前 cashier 会被浏览器记住。 Odoo 用 sessionStorage 按 POS 配置保存“谁是当前收银员”。
  2. 切换后影响的是前台能力,不只是报表归属。 最直接的是改价、打折、正负号输入、现金操作这些按钮会立刻变化。
  3. minimal 角色不是“看起来权限小一点”,而是真的会被前端硬禁用一批入口。
  4. 经理兜底不仅发生在登录阶段,更发生在关店差异、价格控制这类高风险动作上。

这四点连起来看,才是 cashier 切换的真实边界。

为什么说切换 cashier 会留下“浏览器记忆”

pos_store.js 里,Odoo 会把当前 cashier 存到:

sessionStorage.setItem(`connected_cashier_${this.config.id}`, user.id)

并在后续通过 _getConnectedCashier()checkPreviousLoggedCashier() 重新取回。

这意味着什么?

这意味着 Odoo 不是把“当前收银员”当成一次性临时状态,而是把它视为:

  • 这个浏览器标签页当前代表谁在值班,
  • 这个 POS 配置恢复时默认切回谁,
  • 哪个人的前台权限应当立即生效。

所以现实里常见的现象——“刷新页面后还是上一个收银员”“同一台机子总是默认某个人”——很多时候不是 bug,而是这个设计本身就在起作用。

收银员切换后,最先变化的不是名字,而是按钮

很多团队只在乎顶部显示的 cashier 名称,但源码里变化更直接的地方是 ProductScreen 的 numpad 权限。

比如:

  • discount 按钮,在 manual_discount 关闭或 cashier 是 minimal 时会被禁用;
  • price 按钮,除了受 restrict_price_control 控制,还要求当前 cashier 不是 minimal
  • 正负号等高风险输入,在 minimal 角色下也会被限制。

也就是说,切换 cashier 后,前台不是“继续同一套界面,只是换个人名”,而是:

  • 有的人能直接改价;
  • 有的人只能按标准价卖;
  • 有的人能打折;
  • 有的人连折扣键都点不了。

这正是门店常说的“同一台 POS,不同人上机,手感不一样”的根源。

restrict_price_control 真正限制的是谁

pos_store.js 里有个很关键的判断:

cashierHasPriceControlRights() {
    return !this.config.restrict_price_control || this.getCashier()._role == "manager";
}

这段逻辑很值得细品。

它不是说“只要能登录就能改价”,而是说:

  • 如果门店没开启价格控制限制,那么大家都能动;
  • 一旦开启限制,就只有 manager 能改价。

这就是非常典型的 POS 权限边界:

价格修改不是普通销售动作,而是高风险动作。

门店最容易出问题的,往往不是漏点一个商品,而是:

  • 收银员临时改价没留痕;
  • 为了成交随意给低价;
  • 把应该走折扣审批的事,直接做成改价。

所以 cashier 切换真正重要的,不是“订单归谁”,而是“谁拿到了价格控制权”。

为什么 minimal 角色不是装饰性角色

很多人第一次看到 minimal,会以为只是一个“比 cashier 更简化”的展示层概念。

不是。

从菜单和按钮可见性来看,minimal 是很硬的前台操作边界。

navbar.xml 里,像这些入口都会因为 minimal 而被挡住:

  • Install App
  • Cash In/Out
  • 某些需要更强操作能力的菜单项

再结合 numpad 上对:

  • price
  • discount
  • 正负号

这些输入的限制,你会发现 minimal 的真实业务含义更像是:

  • 可以执行标准收银;
  • 但不能碰会改变金额结构、现金结构或终端状态的动作。

所以如果门店说“为什么新人能开单但不能改价、不能现金存取”,不要先怀疑配置坏了。先看他切进去后拿到的是不是 minimal

现金存取按钮为什么也跟 cashier 有关系

pos_store.js 里:

get showCashMoveButton() {
    return Boolean(this.config.cash_control && this.config._has_cash_move_perm);
}

navbar.xml 又额外要求当前 cashier 不是 minimal,才显示 Cash In/Out

这说明 Odoo 对现金动作的态度非常明确:

  • 现金控制本来就是敏感操作;
  • 有没有现金权限是一层;
  • 当前切进来的 cashier 是不是足够高级,又是一层。

这背后保护的不是 UI 美观,而是审计边界。

因为现金存取一旦发生,后面就会牵扯:

  • 钱箱金额变化,
  • 交接责任,
  • closing control 时的差异解释,
  • 最终会计或报表上的责任归属。

所以 cashier 切换不是“谁方便谁来点一下”,而是在决定:谁可以碰门店现金。

为什么经理兜底会在关店时突然变得重要

closing_popup.js 里有个门店最容易忽略的设计:

当盘点金额和系统预期金额不一致时,如果当前用户有足够 authority,就可以看到:

  • 是否继续,
  • 是否把差异记到账上。

如果没有足够 authority,系统会明确提示:

请联系经理接受 closing difference。

这就说明 cashier 切换的边界,到了关店时会进一步收紧。

因为关店差异不是普通交互错误,而是:

  • 门店少钱或多钱;
  • 是否允许带着差异过账;
  • 这笔差异到底按损失还是盈利处理。

所以“经理权限”在 POS 里不是一个泛泛的称呼,它在关键时刻是一个风险签字位

登录、切换、关店,其实是同一条权限链

如果把这些源码放在一起看,会发现一条很清晰的业务逻辑:

  1. 登录或切换 cashier,决定当前是谁;
  2. 当前 cashier 的 _role,决定前台哪些按钮可点;
  3. 高风险动作,例如改价、现金存取、关店差异,进一步看是否需要 manager 级别兜底;
  4. 浏览器把当前 cashier 记下来,保证这套边界不是“点一下就忘”。

这就解释了为什么很多门店觉得:

  • “明明都进 POS 了,为什么功能不一样?”
  • “为什么同一订单 A 能改,B 不能改?”
  • “为什么盘点差异出现后,普通收银员过不去?”

因为 Odoo 不是只在登录时做权限判断,而是在整个前台操作链上不断重新使用 cashier 身份。

常见误解

误解 1:切换 cashier 只是为了统计销售员

不对。

统计归属当然是结果之一,但更直接的影响其实是当前 UI 权限。

误解 2:能看到 POS 就代表有完整收银权限

不对。

minimal 角色就说明:能卖单,不等于能改价、能打折、能碰现金。

误解 3:经理权限只在后台才重要

也不对。

POS 前台本身就把经理当作高风险动作的最后授权位。

误解 4:刷新后还是上一个 cashier,一定是系统脏了

很多时候不是。

有可能只是 sessionStorage 把上次连接的 cashier 恢复出来了。

实战排查顺序

当你遇到“切换收银员后权限不对”的问题时,建议按这个顺序看:

  1. 当前浏览器恢复出来的 cashier 是谁;
  2. 该 cashier 的 _rolemanager、普通 cashier 还是 minimal
  3. POS 配置是否开启了 restrict_price_control
  4. manual_discount 是否开启;
  5. 门店是否开启现金控制,以及当前人有没有 cash move 权限;
  6. 关店差异时,当前人是否具备 authority;
  7. 是权限问题,还是只是前一个 cashier 状态没被正确切走。

最后的理解方式

最值得记住的一句话是:

Odoo POS 的 cashier 切换,本质上是在切换“谁对这台收银机承担当前操作责任”。

名字只是最表面的变化。

真正被切换的是:

  • 改价权,
  • 折扣权,
  • 现金动作权,
  • 关店差异的处理权。

所以在门店制度设计里,cashier 切换绝不只是交接班按钮,而是权限、责任和审计边界一起切换。

DISCUSSION

评论区

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