先说结论
Odoo CRM 的“Convert to opportunity”,不是把 type 从 lead 改成 opportunity 就结束。
源码真正做的是一条有顺序的业务链:
- 先判断这条 lead 该不该直接 merge
- 再决定客户是新建还是挂到现有 partner
- 必要时先处理 commercial partner / parent company
- 然后才执行
convert_opportunity() - 最后再看要不要强制改 salesperson 和 sales team
所以“转商机”本质上不是字段切换, 而是一次 线索资格确认 + 客户归属落位 + 负责人承接 的组合动作。
一、wizard 默认就在帮你猜“该挂谁”
入口在 crm.lead2opportunity.partner。
这个向导不是空白表单。default_get() 和一系列 compute 方法会先做预判:
- 如果当前 lead 已经能匹配到 partner,
action默认就是exist - 如果发现重复线索足够多,
name会默认成merge user_id默认继承 lead 当前负责人team_id会跟着负责人重新找默认 team
这意味着用户看到的默认选项,其实已经带了 Odoo 的业务判断:
这条线索更像是“转成一个新机会”,还是“并入已有客户/已有机会体系”。
所以很多人觉得 wizard “很聪明”,不是错觉,它确实先跑了一轮源码判断。
二、为什么系统经常建议“Link to an existing customer”
_compute_action() 会先调用 lead._find_matching_partner()。
也就是说,Odoo 在转商机前,优先想做的不是“新建客户”, 而是先问:
- 这个邮箱是不是已经有联系人?
- 这个线索是不是其实已经对应某个客户?
在测试里,哪怕客户名称写法不同,只要邮箱归一化后能对上,wizard 也会偏向 exist。
这背后的设计很现实:
如果 CRM 入口很多——网页表单、人工录入、邮件别名、导入—— 同一个客户被重复创建,是最常见的数据污染源之一。
所以 Odoo 的默认姿势不是“多建客户”,而是 尽量复用现有客户主数据。
三、merge 不是“把几条线索装进一个文件夹”
如果 wizard 判断 name='merge',走的是 _action_merge()。
这里的重点有三个:
1. 先把重复机会集合起来
源码里是:
duplicated_lead_ids | lead_id
也就是把当前 lead 和所有判断为重复的机会放到一起处理。
2. 调 merge_opportunity() 选出一个结果记录
真正保留下来的不是“全部拼接”, 而是由合并逻辑选一个结果商机,其他记录成为被吸收方。
3. 最后把其余重复记录删掉
(to_merge - result_opportunity).sudo().unlink()
所以 merge 的本质其实是:
选一个主机会,别的记录把有价值的关联迁过去,然后清掉重复壳子。
这也是为什么 merge 前一定要想清楚“谁做主记录”。 因为主记录会决定很多最后留下来的字段语义。
四、为什么“先处理客户,再 convert”这么重要
_convert_and_allocate() 的顺序非常关键:
_convert_handle_partner()lead.convert_opportunity(...)- 再做 salesperson/team 分配
也就是说,Odoo 不希望先把 lead 转成 opportunity, 再回头想“客户是谁”。
它更偏向先把客户归属钉住:
- 挂现有联系人
- 或创建新联系人
- 必要时带上
commercial_partner_id
然后再转换机会类型。
这背后是因为很多字段同步都依赖 partner:
- 邮箱
- 电话
- 语言
- 地址
- 公司归属
如果 partner 没先落定,后面的 opportunity 语义就容易飘。
五、commercial_partner_id 不是装饰字段
很多实施顾问会忽略向导里的 company 选择,只盯着 contact。
但源码里 _convert_handle_partner() 会把:
force_partner_idcreate_missingwith_parent=self.commercial_partner_id
一起交给 _handle_partner_assignment()。
翻成业务话:
- 你不只是在选联系人
- 你还在决定这个联系人是不是要挂到某个上级公司下面
这会直接影响:
- 新建联系人时的 parent company
- commercial entity 归属
- 后续重复判断和客户层级
所以如果 B2B 场景里公司层级很重要,
转商机时随手忽略 commercial_partner_id,后面数据会越来越乱。
六、负责人分配发生在转换之后,而且不一定强制
转换完成后,源码才开始看 force_assignment。
逻辑是:
- 如果
force_assignment=True,即使已有负责人,也可以改 - 如果
force_assignment=False,只给尚未有负责人的机会分配
这解释了很多现场疑问:
为什么我选了 salesperson,结果有的机会没变?
因为你没有强制覆盖已有负责人。
为什么 team 会跟着 salesperson 变?
因为向导里 team_id 本来就会根据 user_id 重新求默认 team。
所以“转商机”界面的负责人选择,不只是显示值, 而是会影响转换后的承接 pipeline。
七、mass convert 和单条 convert 不是完全一回事
crm.lead2opportunity.partner.mass 额外加了两个关键语义:
deduplicateeach_exist_or_create
也就是说,批量转换时,Odoo 更倾向:
- 先对每条 lead 判断是否要 dedup
- 如果勾选了自动处理,就先 merge 重复项
- 再对每条 lead 单独判断“找现有客户 or 创建客户”
这其实很合理。
因为批量操作里最怕的是:
- 一口气造出一堆重复 customer
- 把重复 lead 各自转成多条机会
所以 mass convert 的核心目标不是“快”, 而是 在批量场景下尽量不把脏数据批量放大。
八、实战里最容易误解的 5 件事
1. 以为 convert 只是类型切换
错。它本质上是客户归属和机会落位流程。
2. 以为 partner 一定是新建
错。源码默认优先复用已有 partner。
3. 以为 merge 是“保留全部信息”
错。merge 一定是主记录优先,其他记录会被吸收甚至删除。
4. 以为 salesperson/team 是最先决定的
错。源码是先处理 partner,再 convert,再分配。
5. 以为批量转换只是单条转换循环执行
不完全是。mass mode 有自己的 dedup 和 each-exist-or-create 语义。
九、一句话记忆法
Odoo CRM 转商机,先决定客户归谁、重复怎么收,再把 lead 变成 opportunity,最后才决定谁来接。
理解这个顺序,你就不会再把“转商机”当成一个简单按钮了。
DISCUSSION
评论区