POS 积分核销

Odoo POS 积分优惠为什么不只是“扫券就抵扣”:reward 领取、核销校验与发券时机讲透

很多人以为 POS 会员优惠就是“顾客出示一张券,收银员点一下抵掉”,但 Odoo 真正处理的是“这张券现在还能不能领、当前价目表能不能用、奖励是在本单即用还是下次发放、哪些新券该打印而哪些不该暴露代码”。本文结合 pos_loyalty 源码,把 reward 领取与核销的真实时机讲透。

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

很多门店会把 POS 里的 loyalty、reward、coupon 想成一个极简流程:

  • 顾客出示卡券或优惠码,
  • 收银员扫进去,
  • 系统扣点数或减金额,
  • 如果触发新奖励,再生成一张新券。

这个流程没错,但太薄了。

Odoo 真正关心的问题比“能不能抵扣”多得多:

  • 这张券是不是还在有效期;
  • 这个 program 有没有到启用日期;
  • 当前 pricelist 能不能用这张券;
  • 余额够不够领取某个 reward;
  • 本单产生的新券是要当场打印,还是只更新卡余额;
  • 什么信息可以给小票,什么信息不该直接暴露。

所以更准确的理解是:

Odoo POS 的 loyalty 不是简单抵扣,而是一套“先校验可领取性,再决定如何核销和如何发放”的时机控制。

先说结论

结合 pos_loyalty/models/pos_config.pypos_loyalty/models/pos_order.py,可以先记住五个关键点:

  1. 顾客输入 coupon code 后,系统先判断这张券是否“现在可用”。
  2. “可用”不只看余额,还看 program 是否启用、是否过期、是否匹配当前 pricelist。
  3. 本单获得的新奖励,不一定都应该把 code 直接打印给顾客。
  4. 礼品卡、电子钱包、未来生效券在输出策略上并不相同。
  5. 核销成功不是终点,Odoo 还要把更新后的 points、program usage 和新券信息回传给 POS 缓存。

顾客输入 code 时,系统检查的不是“有没有这张券”而已

pos_config.py 里的 use_coupon_code() 很能代表 Odoo 的设计思路。

它先按 code 查 loyalty.card,再继续判断:

  • 卡券所属 program 是否仍然 active;
  • coupon 本身有没有 expiration_date
  • program 有没有整体结束日期 date_to
  • program 是否还没开始 date_from
  • 使用次数是否达到上限;
  • 当前 program 有没有任何 reward 还能被领取;
  • 当前价目表是否在 program 允许范围内。

这说明 POS 里的“扫券”根本不是被动读码。

真正发生的是:

收银台在问后台:这张券在此时此地、对这个顾客、在这个价目表下,到底还有没有资格参与这次结账。

所以门店现场常见的几种抱怨:

  • “明明是这张券,为什么扫不进去?”
  • “昨天还能用,今天怎么失效?”
  • “积分还在,为什么说没有 reward 可领?”

都不一定是 bug,很可能只是 Odoo 在执行这些时机校验。

“有点数”不等于“现在能领到奖励”

源码里一个很容易被忽略的判断是:

not any(r.required_points <= coupon.points for r in program.reward_ids)

它的业务含义非常直接:

  • 券上有 points,
  • 不等于当前就一定有 reward 可领。

因为 reward 是 program 规则层的对象,不是 points 本身。

换句话说,Odoo 保护的是两层边界:

  1. 余额层:券上到底还有多少 points;
  2. 规则层:当前 program 里有没有满足条件的 reward。

这对实施顾问和门店主管都很重要。

很多时候“顾客还有积分却兑不了”并不是系统坏了,而是 reward 规则已经改了、停了,或者当前条件不满足。

pricelist 也是核销边界的一部分

use_coupon_code() 还会明确检查:

elif program.pricelist_ids and pricelist_id not in program.pricelist_ids.ids:

这意味着 loyalty 在 POS 里并不是一个脱离商品价格体系独立运行的外挂。

它会受到当前价目表的边界影响。

这在现实门店里非常常见:

  • 某些会员券只允许零售价门店使用;
  • 员工价、批发价、特殊活动价目表下,某些券不允许叠加;
  • 同一顾客在不同店、不同终端、不同价格体系里,能用的奖励不完全一样。

所以当门店说“这张券在 A 店能用,B 店不能用”,先不要急着怀疑码错了。很可能是 program 和 pricelist 的边界在起作用。

本单奖励什么时候算“发出去”

confirm_coupon_programs() 处理的是订单已经创建之后的事情。

这一步里,Odoo 才真正:

  • 创建需要新发的 card;
  • 把已有 card 的 points 更新掉;
  • 把 reward line 回写到对应 coupon;
  • 生成 coupon_updatesprogram_updatesnew_coupon_info

这意味着一个很关键的时机边界:

前台看见“可领”,不代表奖励已经正式发出;真正发出发生在订单落库之后。

这也是为什么一些门店会感觉:

  • 收银员前面已经选了 reward,
  • 但最终订单失败后,奖励并没有真的发出去。

因为 Odoo 不会在“只是选中、还没确认”的阶段就让业务对象真正落地。

为什么有些新券要打印,有些不该打印

confirm_coupon_programs() 返回 new_coupon_info 时,有一段特别关键的过滤:

  • 只考虑 applies_on == 'future' 的场景;
  • gift card 和 ewallet program 不把 code 放进这里打印。

这非常能体现 Odoo 的业务边界感。

它不是“只要发了新奖励,就把所有 code 全打印出来”。

而是会区分:

1)未来使用型奖励

这种奖励通常是:

  • 本单满足条件后获得;
  • 下一单再用;
  • 小票上应该清楚告诉顾客获得了什么、何时到期、code 是什么。

所以它进入 new_coupon_info 很合理。

2)礼品卡 / 电子钱包

源码明确写着:

  • gift_cardewallet,不要在这里把 coupon code 打印到小票。

这不是一个小细节,而是很强的业务判断。

因为礼品卡和电子钱包往往更像:

  • 持久账户,
  • 存值对象,
  • 后续还会反复使用的卡资产。

它们的 code 暴露策略,和“送你一张下次使用的促销券”不是一回事。

换句话说:

Odoo 不仅在管理优惠金额,也在管理哪些奖励信息适合在收银台公开。

核销完成后,为什么还要回传 updates

很多人会觉得:

  • 后台都已经确认了,
  • 前台直接继续卖不就行了?

confirm_coupon_programs() 还会把这些结果回给 POS:

  • coupon_updates
  • program_updates
  • new_coupon_info
  • coupon_report

原因很简单:POS 不是单纯渲染页面,它需要本地继续工作。

如果前台拿不到这些更新,很容易出现:

  • 顾客刚用过的券,前台还显示旧 points;
  • 新发的 reward 小票没法正确展示;
  • 某个 program 的 usage 计数没更新,下一笔判断又错;
  • 收银员以为奖励已发,但本地缓存还停在旧状态。

所以 loyalty 在 POS 场景下真正难的不是“算一下减多少”,而是确认后如何让本地缓存和后台事实重新对齐。

常见误解

误解 1:只要 code 能查到,就一定能用

不对。

还要看激活状态、有效期、使用上限、价目表和 reward 条件。

误解 2:有积分就一定能领 reward

不对。

积分只是余额,reward 领取还受 program 规则控制。

误解 3:新奖励生成后应该全部打印出来

不对。

未来使用型奖励、礼品卡、电子钱包在输出策略上就不同。

误解 4:前台一选 reward,就已经发券成功

不对。

真正的创建、更新和回传发生在订单确认之后。

实战排查顺序

如果你遇到 loyalty 领取或核销异常,建议按这个顺序查:

  1. 这张 card / coupon 是否真的存在且 program 仍 active;
  2. coupon 或 program 是否过期、未开始或超出使用次数;
  3. 当前价目表是否被 program 允许;
  4. 当前 points 是否足以覆盖任何一个 reward;
  5. 该奖励是本单即用还是 future 奖励;
  6. 确认后是否返回了 coupon_updatesnew_coupon_info
  7. 收银台的问题到底是后端没确认,还是前端缓存没更新。

最后的理解方式

如果只记一句话,我会建议记这句:

Odoo POS 的 loyalty 核心不是“扫码抵扣”,而是“判断奖励何时可领、何时可用、何时该发、何时该显示”。

这套时机控制,决定了:

  • 顾客此刻能不能核销;
  • 收银员能不能安全确认;
  • 小票上该不该暴露新 code;
  • 前后台的积分状态能不能保持一致。

这才是 POS loyalty 真正难也真正值钱的地方。

DISCUSSION

评论区

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