先说结论
Odoo CRM 的自动分配,不是“拿到新线索以后立刻随机分给某个销售”。
源码里真正的顺序更像这样:
- 先按 team 的 domain 把可接线索圈出来
- 再按 team 的 assignment_max 做加权分桶
- 挑到候选 lead 后,先检查重复项
- 必要时先 merge,减少脏数据
- 最后才把清洗后的结果交给成员级分配
所以自动分配的设计重点,其实不是“快”, 而是:
别让同一个客户被拆成多条重复 lead,再分别落到不同 team 或不同 salesperson 手里。
一、team 分配和 salesperson 分配,本来就是两步
crm.team._allocate_leads() 和 _assign_and_convert_leads() 是两段不同职责。
第一段先处理:
- 哪些线索应该进入哪个 team
- 多个 team 重叠时怎样按容量分配
- 分配前要不要顺手消重
第二段才处理:
- 这个 team 里的成员谁来接
- round-robin 怎么轮
- quota 有没有超
这意味着 Odoo 很清楚:
team 路由问题 和 个人认领问题 不是一回事。
如果 team 入口都没清干净,就急着给人, 后面再怎么轮转都只是把脏数据平均扩散。
二、为什么源码先做 team 级候选池,而不是见一条发一条
_allocate_leads() 会先为每个 team 建自己的候选池:
- 必须是
team_id=False且user_id=False - 不能是已赢单
- 默认只看最近几天创建的 lead
- 还要满足这个 team 的
assignment_domain
这里有两个业务信号很重要。
1. 自动分配只吃“还没归属”的 live lead
也就是说,它不是全库大扫除。 它更像是一个新线索入口调度器。
2. 每个 team 拿到的是自己的可接范围
如果多个 team 的 domain 有重叠,Odoo 不会粗暴地把一个 team 跑完再跑另一个。 而是通过 weighted random choice,一条一条地在各 team 之间轮着抽。
这让重叠 domain 的 team 仍有机会按容量比例分到 lead, 而不是被先执行的 team 吞掉全部候选。
三、为什么重复检查要发生在 team 级,而不是成员级
源码在 team 预分配阶段就建立 duplicates_lead_cache,
并且在 _allocate_leads_deduplicate() 里优先检查:
- 当前 lead 是否有重复记录
- 这些重复记录是否应该先合并
- 合并后谁做 master
这一步放得很早,非常关键。
因为如果等到 salesperson 级别再发现重复, 问题通常已经变成:
- A 销售拿到一条
- B 销售拿到另一条
- 两个人都开始跟进
- 后面再 merge 时,chatter、会议、附件、归因、负责人语义都更难收口
Odoo 的选择很明确:
先在 team 入口处把“这其实是同一件事”尽量合并掉,再做人力分发。
四、merge 为什么被嵌进自动分配链路里
很多团队把 merge 理解成运营人员事后做的数据治理。
但在 Odoo CRM 源码里,自动分配流程就已经把 merge 当作入口清洗的一部分。
_allocate_leads_deduplicate() 的策略是:
- 无重复:直接把 lead 标成该 team
- 有重复:先把候选 lead 及其重复项一起纳入处理
- 先确保 master 记录也会写入 team
- 再调用
_merge_opportunity(... auto_unlink=False)形成结果记录 - 最后统一 unlink 被吸收的重复壳子
这说明 merge 在这里只是表面上和“自动分配”绑在一起, 本质上做的是 分配前主记录收敛。
这样后面进入成员轮转的,就不再是一堆彼此打架的重复项, 而是更接近真实销售对象的那一条记录。
五、为什么还要专门做 duplicate cache
源码不会在每次抽到 lead 时都重新全库搜重复。
它先在 _allocate_leads() 里把候选 lead 的重复信息尽量缓存起来。
原因很现实:
- 自动分配通常是 cron 场景
- 候选 lead 可能不少
- 每次分配都触发 search/flush,会明显拉长事务
所以 Odoo 的思路是:
- 先批量搜到候选 lead
- 顺手准备重复缓存
- 进入分配循环时直接复用结果
- 按 bundle size 分批 commit
这不只是性能优化, 也是为了避免长事务把整个分配作业拖成“越跑越慢、越跑越危险”。
六、为什么 team 先分桶,不等于 salesperson 已经确定
在 _allocate_leads_deduplicate() 里,Odoo 会先对结果记录调用 _handle_salesmen_assignment(user_ids=None, team_id=self.id)。
这里很容易误解成“已经给销售了”。
但这一步更准确的作用是:
- 先把 lead 放进正确 team
- 确保 merge 结果的
team_id不漂移 - 让后续成员分配有一个干净、一致的 team 归属起点
也就是说,team 归属先确定,个人归属后细分。
这和线下销售管理很像:
先把客户归到华东区团队, 再决定是 Alice 还是 Bob 跟。
七、实战里最容易踩的 4 个坑
1. 以为自动分配只是“平均派单”
错。源码先做 team 入口治理,再做人头分配。
2. 以为 merge 只属于人工数据清洗
错。自动分配本身就把 merge 当成前置步骤。
3. 以为重复 lead 只会影响报表
错。它更直接影响 team 路由和 salesperson 冲突。
4. 以为 round-robin 是自动分配的全部核心
错。真正更难也更关键的是: round-robin 之前到底有没有先把对象清洗成“一个客户一条主线”。
一句话记忆法
Odoo CRM 自动分配不是“先发牌再补卫生”,而是“先按 team 预分桶、先消重收口,再把干净 lead 交给后续轮转”。
DISCUSSION
评论区