先说结论
Odoo 的 abandoned cart 不是“购物车放着不动几小时,然后系统自动发一封提醒邮件”这么简单。
在 website_sale 里,真正发生的是一条完整链路:
- 先判断这张 draft
sale.order算不算弃单; - 再判断它是否真的值得发恢复邮件;
- 发送前确保订单拥有可用的 portal token;
- 把邮件里的按钮直接指向原购物车恢复入口;
- 发送后标记已发,避免重复骚扰。
所以它的本质不是营销定时器,而是:
把中断的结账流程重新接回到原来的交易上下文。
一、为什么弃单是 sale.order 状态,而不是营销名单
sale.order 上有两个关键字段:
is_abandoned_cartcart_recovery_email_sent
_compute_abandoned_cart() 会同时要求:
- 订单属于某个网站;
- 订单状态仍是
draft; - 超过网站配置的 abandoned delay;
- 不是 public partner;
- 订单里还有真正的 order line。
这说明 Odoo 没有另外造一张“弃单营销表”,而是直接在订单生命周期里定义:
- 哪张网站草稿单已经进入值得恢复的阶段。
这样做的好处是,价格、税、客户身份、支付状态都还能沿用原订单语义。
二、为什么“已弃单”不等于“应该发邮件”
真正更值得看的是 _filter_can_send_abandoned_cart_mail()。
它会继续过滤掉这些订单:
- 客户没有邮箱;
- 支付交易已经报错;
- 订单行全是零价;
- 客户在之后已经通过别的订单完成购买。
这一步特别成熟,因为它承认:
- 老购物车并不天然等于优质恢复对象。
例如支付已经报错的单,问题可能根本不在“忘了下单”,而在支付链本身。再给他发恢复邮件,很可能只会制造困惑。
而“后来已经买过”的排除逻辑,则是在避免最差的一种体验:
- 客户都已经下单成功了,系统还追着提醒他“你还有购物车没结账”。
三、为什么 portal token 是恢复链路的核心
_cart_recovery_email_send() 会先执行:
_portal_ensure_token()
而 _notify_get_recipients_groups() 又会把按钮 URL 改写成:
/shop/cart?id=<order_id>&access_token=<token>
这一步非常重要。
因为弃单恢复真正要解决的,不是“发出提醒”,而是:
- 让客户点进来就回到原来那张购物车;
- 而不是重走一次商品选择流程;
- 同时还不能把订单访问暴露得不安全。
所以 token 不是附属细节,它就是恢复动作成立的前提。
四、为什么恢复入口是 cart,而不是 portal 订单详情
Odoo 把入口放回 /shop/cart,而不是某个只读订单详情页。
这很聪明,因为用户真正想做的是:
- 看看还买不买;
- 改数量;
- 删除商品;
- 继续付款。
也就是说,恢复动作要把客户带回的是:
- 仍可继续操作的购物车语境,
而不是一张历史记录式的页面。
五、为什么已发送标记要反复兜底
除了 _cart_recovery_email_send() 会写 cart_recovery_email_sent = True,相关的 mail hook 也会补做标记。
这说明 Odoo 很清楚邮件发送路径是可分叉的:
- 模板发;
- composer 发;
- automation 发;
- 甚至别的扩展链路也可能介入。
所以真正可靠的做法,不是寄希望“所有发送只走一条路径”,而是:
- 在多个出口都把业务状态闭环补上。
六、最容易误解的几个点
误解 1:弃单恢复就是邮件模板功能
不对。核心其实是订单状态、资格过滤与恢复入口。
误解 2:只要是旧 cart 就该发邮件
不对。没邮箱、支付报错、零价单、已重新成交都应该排除。
误解 3:token 只是为了免登录方便一点
不对。它本质上是“安全回到原购物车”的基础设施。
误解 4:发过一次不需要记录
不对。否则自动化很容易重复轰炸用户。
七、做定制时最该保留什么
如果你要改 Odoo 弃单恢复,最值得保留的是:
- 弃单定义继续挂在
sale.order生命周期上; - 发送资格继续做业务过滤,而不是只看超时;
- 恢复按钮继续直达原 cart 且依赖 token;
- 发送后继续有明确 sent 标记。
否则你很容易做出一套“看起来会发邮件,但恢复率并不高”的空壳功能。
最后一句
理解 Odoo 弃单恢复,关键不是邮件文案怎么写,而是看懂这条主链:
draft 网站订单 → 弃单判定 → 发送资格过滤 → portal token 兜底 → 直达 cart 的恢复链接 → 已发送防重复标记。
看懂以后你就会知道,Odoo 在恢复的不是一封邮件,而是一段还没走完的交易。
DISCUSSION
评论区