企业 预约

Odoo 企业版付费预约为什么不是“客户付完钱就生成日历事件”:booking 暂存、发票支付与最终成会讲透

基于 appointment_account_payment 与 website_appointment_sale,讲清付费预约为何先存 calendar.booking,再在支付成功后转成真正会议或销售确认。

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

做线上预约的人最容易有一个直觉:客户选好时段、付完钱,系统立刻生成会议就行。但企业版 appointment_account_paymentwebsite_appointment_sale 明确把这条链拆成了预约暂存最终成会两个阶段。

关键源码:

  • enterprise/appointment_account_payment/models/calendar_booking.py
  • enterprise/appointment_account_payment/controllers/appointment.py
  • enterprise/appointment_account_payment/controllers/payment.py
  • enterprise/website_appointment_sale/models/sale_order.py
  • enterprise/website_appointment_sale/models/calendar_booking.py

一、为什么付费预约先建 calendar.booking,而不是直接建 calendar.event

控制器 _handle_appointment_form_submission() 写得很直白:如果 appointment type 有 payment step,并且产品需要收费或服务追踪,系统会先创建 calendar.booking

原因很现实:

  • 客户可能支付失败;
  • 支付可能处于 pending;
  • 支付前后时间槽可能被别人抢走;
  • 如果过早建 calendar.event,就会把未确认预约同步到日历、通知和后续业务对象。

所以企业版故意把 booking 做成“待确认的预约壳子”。

二、calendar.booking 真正存了什么

这个模型不是简化版 event,而是完整保留了预约转正所需信息:

  • appointment_type_id
  • partner_idguest_ids
  • staff_user_id
  • booking_line_ids(资源 / 容量)
  • appointment_answer_input_ids
  • product_id
  • account_move_id
  • booking_token

也就是说,支付链、容量链、问卷答案链、访客链都先挂在 booking 上,等支付成功后再统一转正。

三、为什么支付前后都还要重新检查可用性

calendar.booking._filter_unavailable_bookings() 是这条链里最容易被忽视、也最关键的防呆点。

它会按时间边界切段,再分别检查:

  • staff user 在该区间的剩余容量;
  • appointment resource 的剩余容量;
  • 如果多种 appointment type 混在同一资源里,还要看 manage_capacity 边界。

这意味着“前台页面上看起来还有空位”不等于“付款完成时仍然有效”。支付是异步过程,Odoo 必须在最终确认前重验一次。

四、纯支付版与网站销售版有什么区别

纯支付版:先开 draft invoice

_redirect_to_payment() 会调用 _make_invoice_from_booking(),给每个 booking 建一张 account.move,再跳到 /payment/pay。支付落地后,appointment_post_payment() 根据付款结果,决定跳去 booking summary 还是最终 event 页面。

网站销售版:由销售单接管最终确认

安装 website_appointment_sale 后,逻辑更进一步:

  • booking 会关联到 sale.order.line
  • sale.order._check_cart_is_ready_to_be_paid() 在支付前再次检查 booking 是否仍可用;
  • sale.order._action_confirm() 才调用 calendar_booking_ids._make_event_from_paid_booking()
  • 生成的 calendar_event_id 会回写到对应 SOL。

因此网站销售场景里,会议生成与其说由“付款回调”直接触发,不如说由销售确认统一收口。

五、为什么支付成功也不一定马上成会

_make_event_from_paid_booking() 里仍会先筛掉不可用 booking;对冲突项:

  • 标记 not_available = True
  • 在 invoice 或 sale order 上记日志;
  • 不盲目创建 calendar event。

这很重要。Odoo 的优先级不是“只要收了钱就硬塞进日历”,而是先维护排程真实度,再让后续补救流程有据可查。

六、最容易误解的点

1. “booking 就是 event 的别名”

不是。booking 是等待支付 / 等待销售确认的过渡对象;event 才是最终会议事实。

2. “支付页能打开,就说明时段已锁死”

不一定。源码明确保留了支付后再验证的逻辑,因为异步支付期间容量可能变化。

3. “失败场景只会发生在支付失败”

也不对。支付成功后,资源冲突、配置变更、容量不足仍可能导致 booking 无法转成 event。

七、实战建议

  • 如果客户卖的是咨询时段、席位或资源型预约,务必解释 booking 和 event 的双阶段模型。
  • 支付成功后若客户没立即收到会议,不要先查支付网关,先查 booking collision 日志。
  • 网站销售版要重点测试购物车里多个预约并存、同产品多时段重复下单的情况,因为 Odoo 特意避免把它们合并成一条购物车行。
  • 任何预约支付项目都应把“支付成功但最终未成会”的补救流程写进运营 SOP。

八、结论

Odoo 企业版付费预约的关键,不是把付款接上,而是在支付、容量和最终会议事实之间保留一个可回退、可重检的 booking 层。这也是它比“表单 + 支付按钮”更可靠的原因。

DISCUSSION

评论区

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