CRM 企业版

Odoo 企业版 CRM/Helpdesk 为什么不是“改个类型”就完了:线索与工单双向转换、消息线程与 UTM 继承边界讲透

很多人以为 Odoo 把 lead 转 ticket、或把 ticket 转 lead,本质只是同一条记录换个业务类型。企业版 crm_helpdesk 的真实做法更谨慎:它会新建目标对象、迁移消息线程与附件、归档原对象,并把客户匹配、团队归属与 UTM 来源按不同规则接过去。

CRM 企业
进阶 开发者 3 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

很多团队第一次看到 Odoo 企业版里的 Convert to Ticket / Convert to Lead,直觉都会很简单:

  • 这不就是把一条业务记录换个类型吗?
  • 最多把客户、描述、负责人带过去就好了吧?

enterprise/crm_helpdesk 的源码并不是这个思路。

它真正做的,是一套非常克制的 “新建目标对象 + 搬迁上下文 + 归档原对象 + 保留追溯关系” 机制。

换句话说,官方并不把这件事理解成:

把同一条记录从 A 表改成 B 表。

而是理解成:

确认当前业务已经不适合留在原漏斗,于是创建一个更合适的新对象承接后续流程,同时把历史沟通和来源信息安全地接过去。

这篇就专门把 crm_helpdesk 里最值得读的边界讲清楚。


一、先说结论:这不是“改类型”,而是“重建承接对象”

无论方向是哪边,crm_helpdesk 都不是原地改模型。

lead → ticket

wizard/crm_lead_convert2ticket.py 会:

  1. 基于 lead 组装新的 helpdesk.ticket 值;
  2. sudo().create() 新 ticket;
  3. 给 ticket 写入来源消息;
  4. 把 lead 的消息线程挪到 ticket;
  5. 把 lead 附件改挂到 ticket;
  6. 在 lead 上留日志;
  7. 归档原 lead。

ticket → lead / opportunity

wizard/helpdesk_ticket_to_lead.py 会:

  1. 基于 ticket 组装新的 crm.lead 值;
  2. 按权限决定建的是 lead 还是 opportunity;
  3. sudo().create() 新商机对象;
  4. 给新对象写来源消息;
  5. 把 ticket 的消息线程挪到 lead;
  6. 把 ticket 附件改挂到 lead;
  7. 归档原 ticket;
  8. 再回 ticket 上补一条“已转换”的消息。

所以最重要的一句话是:

Odoo 企业版的双向转换,本质是“新对象承接流程”,不是“旧对象变身”。

这会直接影响你对审计、二开和统计口径的理解。


二、为什么官方宁可“新建 + 归档”,也不做原地变形

这套设计看起来麻烦,其实非常合理。

因为 lead 和 helpdesk ticket 虽然都长得像“客户问题单”,但它们服务的是两条完全不同的业务链:

  • crm.lead 面向销售资格判断、商机推进、团队分配;
  • helpdesk.ticket 面向服务受理、客服协作、工单处理。

如果强行让一条记录原地换模型,马上就会遇到一堆问题:

  • 原模型字段怎么处理?
  • 消息线程和附件算谁的历史?
  • 旧报表到底要不要把它继续算进来?
  • 权限与菜单上下文是否还成立?

而“新建 + 归档”的好处是很稳定的:

  1. 流程切换清晰:后续操作发生在新对象上;
  2. 历史仍可追溯:原对象没消失,只是归档;
  3. 消息链不断裂:讨论和附件被迁过去;
  4. 统计边界更干净:CRM 看 CRM,Helpdesk 看 Helpdesk。

官方实际上是在用结构设计告诉你:

转换不是字段编辑,而是业务归属迁移。


三、lead 转 ticket 时,哪些数据会被带过去

action_lead_to_helpdesk_ticket() 里准备 ticket 值的逻辑很克制,核心包括:

  • name
  • description
  • team_id
  • partner_id
  • campaign_id
  • medium_id
  • source_id
  • partner_name
  • partner_phone
  • partner_email

这里最值得注意的是三层语义。

1)问题内容会继承

lead 的标题和描述直接进入 ticket,说明官方认为:

  • 这条咨询本身没变;
  • 变的是它现在更应该由服务台处理。

2)客户上下文会尽量补齐

如果 lead 已经有 partner_id,直接带过去。

如果没有,但存在 partner_namecontact_name,向导会先调用:

  • lead._handle_partner_assignment(create_missing=True)

也就是说,在需要落 helpdesk ticket 前,系统会优先把客户关系补成一个正式 partner,而不是让 ticket 只挂一堆散乱文本。

3)UTM 来源不会丢

campaign_idmedium_idsource_id 会原样延续到 ticket。

这点非常关键,因为它表明官方把 ticket 看成这条业务来源链的后续承接对象,而不是与营销来源完全断开的客服孤岛。

所以你后面追“这张工单原本从哪个活动/渠道来”,在转换后依然有据可查。


四、lead 转 ticket 时,客户信息并不是简单复制字段

这条链路里最容易被忽略的,是 partner_name / partner_phone / partner_email 的处理顺序。

源码逻辑大致是:

  • 优先用 lead 上已经确定的 partner_id
  • 若还没有 partner,但有联系人或客户名,就先触发 partner assignment 创建/匹配客户;
  • partner_phone 优先取 lead.phone;
  • 没有 lead.phone 时,再退回 partner.phone;
  • partner_email 优先取 lead.email_from。

这说明 ticket 侧拿到的不是“原字段原样复制”,而是经过一次 客户对象优先、散字段兜底 的整理。

所以 ticket 上最终保存的,是更适合客服处理的客户入口信息,而不是 CRM 那套半结构化线索文本的照抄。


五、为什么 lead 转 ticket 后,ticket 默认不继承负责人

这个设计非常有意思。

在新 ticket 的 vals 里,源码明确写的是:

  • user_id: None

也就是说,哪怕原 lead 上已经有销售负责人,转成 helpdesk ticket 后,官方也不会自动把销售员继续当成工单负责人

这背后的业务判断很成熟:

  • lead 的负责人,是销售跟进责任;
  • ticket 的负责人,是服务处理责任。

两者并不天然等价。

如果系统默认沿用 lead 的 user_id,很容易把销售误变成客服受理人,造成队列污染。

所以官方宁可只保留 team_id,让工单进入正确 helpdesk 团队,再由服务流程重新分配。

一句话概括:

lead 转 ticket 时,团队上下文可以继承,但个人处理责任不会偷着继承。


六、ticket 转 lead / opportunity 时,哪些数据会被带过去

反方向的 action_convert_to_lead() 同样很克制,创建 crm.lead 时主要带过去:

  • name
  • partner_id
  • team_id
  • user_id
  • description
  • email_cc
  • campaign_id
  • medium_id
  • source_id
  • 如果没有 lead 权限,则额外设 type='opportunity'

这里有两个特别重要的点。

1)helpdesk ticket 进 CRM 时,官方保留的是销售可用上下文

比如:

  • 客户是谁
  • 这条需求来自哪个渠道
  • 内容描述是什么
  • 应该归哪个销售团队
  • 谁来跟

这说明 ticket 转 CRM 的目标,不是保存工单一切细节,而是把它整理成 销售可继续推进的商业对象

2)“转成 lead 还是 opportunity”由权限组决定,不是单纯 UI 文案差异

action_convert_ticket_to_lead_or_opportunity() 会根据用户是否拥有 crm.group_use_lead

  • 有:按钮显示 Convert to Lead
  • 无:按钮显示 Convert to Opportunity

并且在真正创建时,没 lead 权限的用户会把 type 直接设为 opportunity

所以这不是“文案不同但数据一样”,而是 CRM 流程粒度真的不同


七、ticket 转 lead 时,客户匹配规则比很多人想得更保守

models/helpdesk_ticket.py 里的 _find_matching_partner() 很值得读。

它大致按这个顺序找客户:

  1. 如果 ticket 已有 partner_id,直接用它;
  2. 否则若有 email_cc,按标准化后的邮箱匹配 res.partner.email_normalized
  3. 否则若有 partner_phone,按电话找 partner;
  4. 如果还是没有,且要求 force_create=True,才创建新客户;
  5. 找到客户后,可按需要把缺失的 email / phone 回填到 partner。

也就是说,官方态度不是“只要转换就一定造一个客户”。

而是:

  • 先认已有客户;
  • 再按邮箱/电话谨慎匹配;
  • 只有明确需要时才创建。

这种保守做法非常重要,因为服务台最容易产生:

  • 一次性邮箱来件
  • 抄送邮箱
  • 非标准电话
  • 名称写法不稳定

如果这里太激进,CRM 会被迅速污染出大量重复客户。


八、消息线程为什么要整体迁移,而不是只写一条来源备注

双向转换里最值钱的设计,其实不是字段搬运,而是:

  • message_change_thread()

无论 lead → ticket 还是 ticket → lead,源码都会把消息线程整体迁过去。

测试也专门验证了:

  • 原对象上发过的评论消息,转换后应该出现在新对象的 message_ids 中。

这件事意义非常大。

因为业务转换时,真正不能丢的常常不是标题和客户,而是:

  • 客户到底说过什么;
  • 团队内部讨论过什么;
  • 哪一步已经答复;
  • 哪些附件、邮件、评论构成上下文。

如果只新建目标对象再写一句“来源于 XXX”,用户还得来回翻旧对象找上下文,协作就断了。

所以 Odoo 这里做的是更强的承诺:

不是只保留来源引用,而是把讨论上下文一起带去。

这也是这套功能最有企业价值的地方之一。


九、附件为什么也要跟着改挂

源码还会专门搜索:

  • ir.attachmentres_modelres_id 指向原对象的附件;
  • 然后把它们统一改挂到新对象。

这说明官方非常清楚:

  • 转换不是“复制一份附件引用”;
  • 而是要让目标对象真正成为后续工作的主容器。

否则你会遇到很麻烦的情况:

  • 新 ticket / lead 打开后看不到历史附件;
  • 团队以为资料丢了;
  • 还得回旧对象翻证据。

把附件改挂过去后,新的承接对象才能真正独立工作。

而原对象归档,就更像一张“历史转出凭证”。


十、归档不是删除,而是把“当前主战场”切走

双向转换最后都会把原对象归档:

  • lead 被转成 ticket 后,lead active=False
  • ticket 被转成 lead/opportunity 后,ticket active=False

这一步特别容易被误解成“旧对象没用了”。

其实不是。

归档的真实含义是:

  • 它退出当前主流程;
  • 但历史仍然保留;
  • 统计和默认列表也不会继续把它当成活跃待办。

这是一种很成熟的运营边界:

  • 业务焦点移交给新对象;
  • 历史审计仍然存在;
  • 但团队不会因为两个活跃对象并存而重复处理同一件事。

十一、team 与 user 的处理,体现了“团队先于个人”的设计顺序

helpdesk.ticket.to.lead 向导里还有两个很关键的 compute:

  • _compute_team_id()
  • _compute_user_id()

你会发现官方在 ticket → lead 时强调的是:

  1. 先定销售团队;
  2. 再看 ticket 原负责人是否属于该销售团队;
  3. 如果不属于,再决定是否给默认销售员。

而且当规则分配启用时,系统不会草率替你塞默认 user_id

这说明在 CRM 侧,官方优先保证的是:

  • 记录先进入正确团队池;
  • 再由团队规则或负责人机制承接;
  • 而不是把 helpdesk 当前处理人直接硬塞成销售。

这和前面 lead → ticket 时“不要偷继承负责人”是同一种哲学。

跨流程迁移时,团队上下文比个人归属更稳定。


十二、这套转换机制最容易踩的 5 个误区

误区 1:以为转换后原对象会继续正常活跃

不会。官方默认就是归档原对象,避免双边并行处理。

误区 2:以为负责人会自动无缝继承

不会。尤其 lead → ticket 时,源码刻意把 user_id 置空。

误区 3:以为只迁字段,不迁讨论历史

不是。消息线程和附件都是迁移重点。

误区 4:以为 ticket 转 lead 一定创建新客户

不是。先匹配已有 partner,必要时才建。

误区 5:以为 UTM 到了客服就没意义

恰恰相反。campaign / medium / source 双向都能保留,说明官方认可来源链在客服与销售之间持续有效。


十三、对实施和二开的真正启发

1)不要把这类需求做成“直接改模型字段”

官方方案已经说明,真正稳的是新建承接对象,而不是把原对象结构打穿。

2)如果你要自定义转换,优先守住三样东西

  • 消息线程
  • 附件
  • 归档边界

这三样比多搬几个普通字段更重要。

3)负责人自动继承一定要谨慎

销售负责人 ≠ 客服负责人。强行继承往往比手动分配更危险。

4)来源字段值得保留

哪怕业务从销售转到客服,或从客服回到销售,UTM 仍然是跨漏斗分析的重要线索。

5)把“转换”理解成流程重新归属,而不是数据重写

一旦这样理解,你的报表、权限、自动化和审计设计都会更稳。


一句话记忆法

Odoo 企业版 crm_helpdesk 的 lead/ticket 双向转换,本质不是改类型,而是新建承接对象、迁移消息与附件、保留来源与客户上下文,并归档原对象。


参考源码

  • enterprise/crm_helpdesk/models/helpdesk_ticket.py
  • enterprise/crm_helpdesk/wizard/helpdesk_ticket_to_lead.py
  • enterprise/crm_helpdesk/wizard/crm_lead_convert2ticket.py
  • enterprise/crm_helpdesk/tests/test_crm_helpdesk_convert_lead.py
  • enterprise/crm_helpdesk/tests/test_crm_helpdesk_convert_ticket.py
  • enterprise/crm_helpdesk/views/crm_lead_views.xml
  • enterprise/crm_helpdesk/wizard/helpdesk_ticket_to_lead_views.xml
  • enterprise/crm_helpdesk/wizard/crm_lead_convert2ticket_views.xml

DISCUSSION

评论区

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