活动报名

Odoo 官网活动报名为什么不是“填人数去付款”这么简单:门票入购物车、席位校验与免费单自动确认链路讲透

很多人把 Odoo 官网活动报名理解成 event 表单外加支付页,但 website_event_sale 实际把票种、场次、购物车行、席位余量、匿名地址补建和免费单自动确认串在了一起。

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

先说结论

Odoo 官网活动报名,如果涉及收费门票,就不再是“提交报名表单,再跳到支付”这么线性的流程。

website_event_sale 的真正做法是:

  • 先把报名里选中的票种、场次和人数整理出来;
  • 再把它们落成购物车里的 sale.order.line
  • 报名人与订单、订单行、event ticket 一一挂上;
  • 如果门票免费且当前没有既有购物车,可以直接跳过销售与支付;
  • 如果有收费票,系统又会继续检查匿名地址、checkout 与库存/席位边界。

所以活动报名在 Odoo 里并不是 event 子系统单打独斗,而是:

一旦进入收费模式,报名流程就会主动并入网站销售订单体系。


一、为什么 Odoo 先处理“门票”,再处理“报名人”

_create_attendees_from_registration_post() 很能说明问题。

如果这次报名数据里根本没有 event_ticket_id,系统会回到普通活动报名逻辑。

但只要存在 ticket,流程就变了:

  1. 先把所有报名项里的 event_ticket_id 抽出来;
  2. 建立 ticket 映射;
  3. 判断这些票是不是全免费;
  4. 如果不是,就先确保当前有 sale.order 购物车。

这说明 Odoo 在收费活动里,优先解决的不是“报名表单长什么样”,而是:

  • 这笔交易要不要进入销售单;
  • 哪些报名人对应哪种票;
  • 同一票种在同一场次到底买了几张。

也就是说,报名人信息只是前台输入; 真正决定链路的是门票和订单关系。


二、为什么票会按“场次 + 票种”聚合到购物车

源码里把 tickets_data 的 key 设计成:

  • (event_slot_id, event_ticket_id)

然后再对每组数量调用 _cart_add()

这意味着 Odoo 不是给每一个报名人都单独加一条 cart line,而是先按:

  • 哪个场次;
  • 哪个票种;
  • 总共几张;

进行归并。

这么做非常合理。

因为购物车本质上是交易容器,不是报名问卷。

  • 报名人细节适合挂在 registration 上;
  • 订单行适合承载“买了几张某类票”。

如果不分层,订单会很快变成一堆几乎不可维护的冗余行。


三、为什么席位校验不是最后支付时才统一做

sale.order._verify_updated_quantity() 在 event ticket 场景下被专门扩展了。

如果带了 event_ticket_id,它会:

  • 检查 ticket 是否存在;
  • 场次 slot 是否存在;
  • 计算该票当前剩余席位;
  • 对比这次新增数量;
  • 若已售罄则不允许继续增加;
  • 若超出剩余量,则自动截到最大可买数量并返回 warning。

这说明 Odoo 不想等到支付最后一步才告诉用户“票没了”。

而是尽量在购物车更新阶段就把边界收紧。

这对官网活动尤其重要,因为:

  • 活动票常常是稀缺资源;
  • 用户对“临门一脚才报错”最敏感;
  • 报名页如果反馈太晚,转化和口碑都会受影响。

四、为什么免费票可以自动确认,而收费票一定要进入 checkout

registration_confirm() 里这段分支特别关键。

如果订单里根本没有收费 ticket,或者总金额为 0,系统会:

  • 直接 action_confirm()
  • sale_reset() 清掉当前网站购物车;
  • 写入 sale_last_order_id
  • 跳去 /shop/confirmation

但只要订单有金额,系统就会:

  • sale_last_order_id 记进 session;
  • 引导去 /shop/checkout?try_skip_step=true

也就是说,Odoo 没把“活动报名”做成和“销售单支付”并行的第二套流程。

它的思路是:

  • 免费票本质上不需要支付确认,可以快速收口;
  • 收费票则应复用成熟的 checkout / payment 链路。

这让活动和电商在支付层保持一致,后期维护成本更低。


五、为什么匿名用户买票时,系统还会顺手补客户地址

如果 order_sudo._is_anonymous_cart() 且订单有金额,源码会调用:

  • CustomerPortal()._create_or_update_address(...)

并把报名数据里的第一位报名人信息拿去补订单 partner_id

这个设计很聪明。

因为活动报名里,很多时候你还没正式登录,但又必须进入支付。

如果继续保持公共用户身份:

  • 税、账单、确认邮件、后续订单查询都会很别扭。

所以 Odoo 在进入 checkout 前,尽量用报名人信息把匿名单“扶正”为一个真实客户订单。


六、为什么减数量时还要取消多出来的报名记录

_cart_update_order_line() 在 event ticket 场景下还有一层补充:

  • 如果订单行数量减少了,系统会找到超出的 event.registration
  • 按创建时间顺序把多出来的那几条 action_cancel()

这件事非常关键。

因为活动票不是普通商品:

  • 订单行数量变化,背后对应的是座位、报名名额和 attendee 记录;
  • 如果只改 sale order line,不同步 cancel registration,活动人数会立刻失真。

所以 Odoo 明确保证:

交易数量和报名人数不能各走各的。


七、实施时最容易误解的地方

1. 以为活动报名和购物车是两套平行逻辑

不对。收费票场景下,报名会主动并入 sale.order

2. 以为一个报名人就该一条销售订单行

不对。订单行按票种/场次聚合,报名人信息另挂 registration。

3. 以为席位校验只在最终付款前做

不对。更新购物车数量时就已经开始校验。

4. 以为免费票和收费票只差一个金额字段

不对。免费票可以直接确认,收费票必须进入 checkout 生命周期。


最后总结

Odoo 的 website_event_sale 真正做的是:

  • 用 event ticket 定义“卖什么”;
  • 用 sale order 定义“买了多少”;
  • 用 registration 定义“谁来参加”;
  • 用席位校验和自动取消保证数量一致;
  • 用 checkout / payment 把收费报名接入统一交易链路。

所以它不是“活动页面带支付”,而是:

把官网活动报名做成了一个以门票和席位为核心的销售型报名系统。

DISCUSSION

评论区

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