POS 开台控制

Odoo POS 开台为什么不是“输个备用金就进前台”:opening control、cashbox 与 session 命名边界讲透

很多人把 POS 开台理解成“录入起始现金后点开始”,但 Odoo 真正在处理的是会话状态切换、现金控制日志、opening notes 留痕、序列号命名以及“哪些开台中会话允许被取消”。本文结合 point_of_sale 源码讲清:为什么 opening_control 不是摆设、为什么同样是未营业会话有的能删有的不能删,以及开台异常时该按什么顺序排查。

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

很多门店上线 POS 时,最容易轻视的步骤不是收银,也不是关店,而是开台。

表面上看,收银员只是录一个起始现金,然后进入前台;但在 Odoo 里,开台其实是在把“今天这班、这台机、这套现金控制规则”正式变成一个可入账的会话容器。

先说结论:opening_control 的核心作用不是拦你一下再放行,而是把“未开始营业”和“已经开始承担交易责任”明确切开。 一旦跨过这条线,会话就会拿到正式时间戳、正式 sequence 名称、现金控制留言,很多后续动作也随之变得不可逆。

先把状态机看清:不是一打开就叫 opened

pos.session 在源码里有四个关键状态:

  • opening_control
  • opened
  • closing_control
  • closed

很多团队以为创建 session 就等于“营业中”,其实不是。新会话默认先落在 opening_control

这代表它只是一个“待确认开台”的草稿容器,还没正式开始承接当天交易。源码这样设计,是为了让门店在真正开门前,先把这些问题确认掉:

  • 今天的备用金到底是多少;
  • 是否存在现金支付方式;
  • 是否需要留开台备注;
  • 会话能否被安全地取消重来。

set_opening_control() 真正做了什么

pos_session.py 里,set_opening_control() 不是简单写个金额,它至少做了三件很关键的事:

  1. 调用 _set_opening_control_data(),把状态从 opening_control 切到 opened
  2. 记录 start_at,也就是这班正式开始的时间;
  3. 根据 ir.sequence 生成正式 session 名称。

这意味着:开台不是补充元数据,而是会话从“待开始”切到“已营业”的分水岭。

很多实施项目里,大家只盯着收银前端能不能进入,却忽略了后台账务和审计语义其实就是从这里起步的。

现金控制为什么要在开台时留痕

如果当前 POS 配置里存在 is_cash_count 的支付方式,Odoo 在 _set_opening_control_data() 中会:

  • 接收前端传来的 cashbox_value
  • opening_notes 存到 session;
  • 计算 difference = cashbox_value - cash_register_balance_start
  • 调用 _post_cash_details_message() 把差异、理论值、实盘值和备注记进 chatter。

这一步非常重要,因为它说明 Odoo 对开台金额的理解不是“门店今天有多少钱”,而是:

今天这班开始时,现金抽屉被谁、以什么数额、带着什么说明接手了。

所以如果你在后台看到 Opening cash 的消息,不要把它当普通备注。那其实是 cash control 的审计锚点。

为什么没现金支付时,开台逻辑会变轻

源码里还藏着一个很容易忽略的边界:如果当前配置没有现金支付方式,set_opening_control() 依然会把 session 打开,但不会走现金差异那套逻辑。

这时如果有 notes,系统只是简单 message_post 留下一条 opening control message。

这说明 Odoo 的设计是分层的:

  • 有现金控制:开台要确认现金抽屉责任;
  • 无现金控制:开台更像“开始营业”的确认动作。

所以“为什么有的门店开台要录现金,有的不用”,答案往往不在前端界面,而在支付方式配置是否包含现金 counting。

为什么有的开台中会话能删,有的不能删

delete_opening_control_session() 的约束很硬:只有同时满足这两个条件,才允许删除:

  • 状态仍然是 opening_control
  • 该 session 还没有任何订单。

换句话说,只要会话已经正式 opened,或者哪怕还在 opening_control 但已经挂上订单,它就不能被当成“作废草稿”直接删掉。

这条规则背后的逻辑很朴素:

  • 还没开始、也没发生交易,可以重来;
  • 一旦开始营业,或者已经有业务数据进入,就必须保留痕迹。

这也是很多人遇到“为什么后台删不掉这个 session”时最容易误判的点。系统不是卡你,而是在防止把已承担责任的会话当普通草稿擦掉。

session 名称为什么要等开台后才正式分配

set_opening_control() 结尾会去找 ir.sequence(code='pos.session'),然后为 session 生成正式名称。

这件事表面不起眼,实际上意义很大:

  • 开台前,会话只是临时壳;
  • 开台后,它才获得正式业务编号;
  • 这个编号随后会出现在后台查询、审计与运营沟通里。

所以如果你发现某些异常会话名字还是 / 或表现得很奇怪,优先不要怀疑前端显示问题,而要先看会话是否真的完成了 opening control。

最容易误解的三件事

误区一:开台只是 UI 上的一个确认弹窗

不对。它实际在切状态、记时间、写现金日志、分配正式编号。

误区二:开台金额只是为了后面好看一点

也不对。这个金额会影响现金理论余额计算,是 closing control 的起点之一。

误区三:还没卖东西的 session 都可以删

不完全对。只有仍处于 opening_control 且无订单的 session 才能删;一旦 opened,就不再是“随手可扔”的草稿。

实战排错顺序

遇到“开不了台 / 开台后编号异常 / 不能删除会话 / 现金起始额不对”,建议按这个顺序查:

  1. 先看 session 当前状态,是 opening_control 还是已经 opened
  2. 再看 POS 配置里是否存在现金支付方式;
  3. 看前端传入的 cashbox_value 与后台 cash_register_balance_start 是否一致;
  4. 看 chatter 里有没有 Opening cash / opening notes 留痕;
  5. ir.sequencepos.session 配置是否正常;
  6. 如果删不掉,确认该 session 是否已有订单,或是否已跨过 opening_control。

最后的结论

POS 开台最容易被低估,是因为它看起来不像“交易动作”。但在 Odoo 里,它其实决定了:

  • 从什么时候开始正式营业;
  • 现金责任从哪一刻开始计算;
  • 这个会话是否能被当草稿取消;
  • 后续 closing control 的基线从哪里起算。

所以不要把 opening control 当成“进系统前多点一次确认”。

它真正做的,是把“准备营业”变成“开始对今天这班交易负责”。

DISCUSSION

评论区

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