先说结论
Odoo CRM 里“线索一直没人接”,很多时候不是 cron 坏了,而是线索在某一层筛选里被自然挡住了。
源码里的真实顺序不是“看到 lead 就直接派给销售”,而是:
- 先看 team 能不能参加本轮分配;
- 再看这条 lead 能不能进入 team 候选池;
- 进入 team 后,再看有没有成员仍有 quota 且没 opt-out;
- 然后再用 preferred domain / assignment domain 做成员匹配;
- 只有真正命中成员后,系统才会
convert_opportunity()并写入负责人。
所以排查时最怕的,不是“什么都不懂”,而是一上来只盯着 cron 日志。真正高频的问题,常常出在配置漏斗本身。
一、第一步先别看成员,先看 team 有没有资格参加
_cron_assign_leads() 并不是对所有团队一视同仁。
它先筛 team:
- 这个 team 必须启用了 leads 或 opportunities;
- team 自己不能
assignment_optout=True; - 后续
_allocate_leads()里如果assignment_max=0,这个 team 也会直接被跳过。
这意味着:
只要 team 级别已经被排除,后面成员 quota、成员 domain 配得再漂亮,都不会生效。
很多实施现场的误判就在这里: 用户看到成员还在、销售也在线,就以为自动分配应该继续跑;但其实 team 级别已经暂停了,后面的成员逻辑根本没有入场机会。
二、第二步看 lead 有没有进候选池,而不是先问“为什么没人接”
_allocate_leads() 对候选线索有一层非常硬的过滤:
team_id=Falseuser_id=False- 不是 won
create_date不能晚于crm.assignment.delay允许的时间- 如果 cron 走默认参数,还要求创建时间落在最近
creation_delta_days=7天内 - 还要满足 team 的
assignment_domain
这几条合起来,已经能解释大量“看起来像漏派”的案例。
典型误区 1:线索已经挂了 team 或负责人,还以为会再自动分配
不会。
team 分配阶段只吃“没有 team、没有 user”的线索; 成员分配阶段则只吃“已有 team,但还没有 user,也还没 date_open”的线索。
也就是说,一旦线索已经被某段流程手工认领、接口写入负责人,或者提前带上了 team,它走的就不是同一条入口了。
典型误区 2:新线索刚进来几秒,为什么没立刻分派
因为源码支持 crm.assignment.delay。
它的设计目的,就是给其它自动化规则留缓冲时间,让系统先完成来源清洗、打分、补字段或去重准备,再把 lead 送进自动分配。
所以“刚创建 = 应该马上派单”本来就不是 Odoo 的默认世界观。
三、第三步再看成员:不是有人在 team 里,就代表他本轮能接单
到了 _assign_and_convert_leads(),源码先做的不是分配,而是筛成员:
assignment_optout=False_get_assignment_quota(...) > 0
只要这两条不满足,成员就不会进入 members_to_assign。
换句话说:
“成员还在团队里” ≠ “本轮还能接线索”。
1)Pause Assignment 是最直观也最容易漏看的开关
成员勾了 Pause Assignment,他仍然属于 team,但本轮会被完全跳过。
这很适合请假、培训、短期停接线索的场景; 也因此特别容易造成一种错觉:
- 组织架构没变;
- 销售还在 team 名单里;
- 但系统就是不给他分线索。
答案往往不是 bug,而是他被 opt-out 了。
2)quota 归零后,成员也会像“隐身”一样消失
crm.team.member._get_assignment_quota() 并不是拿 assignment_max 当“今天最多多少条”。
它先把 assignment_max 折算成 30 天平均能力,再用最近 24 小时已分配数量去抵扣。
因此常见现象是:
- 月容量看起来不小;
- 但今天前面几轮 cron 已经分过;
- 结果这轮
_get_assignment_quota()返回 0 或负数; - 成员直接被排除,后面匹配不到人。
从业务感觉上看,这像“系统没继续派”; 从源码逻辑上看,这其实是系统在主动限流。
四、第四步才轮到 domain:很多线索不是没人,而是没人“有资格”接
当可分配成员被筛出来后,源码不会立刻 round-robin。
它会先做两层漏斗:
- preferred domain 优先池
- 普通 assignment domain 池
并且 lead 会按 probability 从高到低排序去命中成员。
这里最关键的不是“轮转”,而是“有没有命中任何一个成员的 domain”。
preferred domain 太窄时,会出现什么
源码会先找同时满足:
- 成员
assignment_domain - 成员
assignment_domain_preferred
的 lead,先做一轮优先分配。
这本意是把更匹配的线索优先喂给更适合的人; 但如果 preferred domain 设得过窄,就会出现:
- 你以为系统会优先给某销售;
- 实际上根本没有 lead 命中这组条件;
- 然后它才退回普通 domain 分配。
assignment domain 太严时,会出现真正的“堆积”
第二轮里,源码为每个成员计算:
to_assign.filtered_domain(member.assignment_domain)
如果某条 lead 对所有成员都不命中,那么这条线索就会留在 to_assign 里,本轮谁也接不到。
这类问题看起来最像“cron 没跑”,因为:
- team 明明拿到了 lead;
- 但最终没有 user 被写进去;
- 线索也没有被 convert 成 opportunity。
实际上不是任务没执行,而是执行完后没找到合法接手人。
五、为什么“没有负责人”时连商机都看不到
_assign_and_convert_leads() 里真正落库的关键动作是:
- 找到命中的成员;
- 调用
convert_opportunity(..., user_ids=member.user_id.ids)。
所以如果没有成员命中:
- 不会写负责人;
- 不会转 opportunity;
date_open也不会按这条链路推进。
这就是为什么有些团队会说:
“线索都进来了,但销售漏斗里没看到新增商机。”
很多时候原因并不是“转商机按钮坏了”,而是前面的成员筛选根本没成功。
六、正确的排查顺序,不要一上来查 cron
如果你在 CRM 里看到未分配线索堆积,建议按这个顺序看:
1. team 级别
- team 是否
assignment_optout - team 是否启用了 leads / opportunities
- team 的
assignment_max是否大于 0 - team 的
assignment_domain是否把候选范围卡得太死
2. 线索入口
- lead 是否仍然
team_id=False且user_id=False - lead 是否已赢单 / 非 live
create_date是否被crm.assignment.delay挡住- lead 是否早于 cron 默认看的 7 天窗口
3. 成员级别
- 成员是否
Pause Assignment _get_assignment_quota()是否已经耗尽- 成员
assignment_domain是否过严 assignment_domain_preferred是否窄到几乎永远命不中
4. 结果验证
- 是否真的写入了
user_id - 是否因此触发了
convert_opportunity() - 是否仍有 team 内未认领 lead 卡在
date_open=False
这个顺序的好处是:
从“有没有资格入场”一路查到“有没有命中接手人”,不会在错误层级绕圈子。
七、实战建议:把“平均发牌”思维改成“漏斗诊断”思维
很多公司在理解线索分配时,还停留在“系统应该自动平均发给销售”的直觉里。
但 Odoo CRM 的源码显然不是这么设计的。
它更像一个多层漏斗:
- 先过滤不该进来的线索;
- 再限制今天不该继续接单的人;
- 再按匹配规则把合适 lead 送到合适成员;
- 找不到合法成员时,就宁可不分,也不瞎发。
这套设计未必让结果“看起来平均”,但它非常接近真实业务里的约束式分配。
最后一句话总结
Odoo CRM 里线索堆积,通常不是“没人跑任务”,而是线索在 team 资格、时间窗口、成员 quota 或 domain 匹配中的某一层漏斗里被挡住了。
DISCUSSION
评论区