CRM 深度

Odoo CRM 自动分配为什么会先‘消重再发牌’:team 预分桶、重复合并与 round-robin 前置清洗讲透

很多人以为 Odoo CRM 的自动分配只是按照 team quota 把线索平均塞给销售。源码真正先做的是 team 级别的预分桶、候选线索去重,以及把重复记录先合并,再把干净结果交给后续成员分配。

CRM
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

Odoo CRM 的自动分配,不是“拿到新线索以后立刻随机分给某个销售”。

源码里真正的顺序更像这样:

  1. 先按 team 的 domain 把可接线索圈出来
  2. 再按 team 的 assignment_max 做加权分桶
  3. 挑到候选 lead 后,先检查重复项
  4. 必要时先 merge,减少脏数据
  5. 最后才把清洗后的结果交给成员级分配

所以自动分配的设计重点,其实不是“快”, 而是:

别让同一个客户被拆成多条重复 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=Falseuser_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 的思路是:

  1. 先批量搜到候选 lead
  2. 顺手准备重复缓存
  3. 进入分配循环时直接复用结果
  4. 按 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

评论区

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