Odoo 自动化规则全面深度解析

发布于:2025-05-27 ⋅ 阅读:(119) ⋅ 点赞:(0)

一、自动化规则入门指南:让重复工作自动化

欢迎来到Odoo的世界!Odoo是一款功能强大的企业管理软件,其灵活性和可定制性深受用户喜爱。今天,我们将一起探索Odoo中一个非常实用的功能——自动化规则 (Automated Actions)。通过本指南,你将理解它是什么,它如何工作,以及它能为你的企业带来哪些价值。

一、什么是Odoo自动化规则 (Automated Actions)?

想象一下,在你的日常工作中,总有一些固定的、重复性的操作。比如:

  • 每当创建一个新的客户时,都需要指派一位销售负责人。
  • 每当一个销售订单确认后,都需要自动向客户发送一封感谢邮件。
  • 每当一个任务的状态变更为“已完成”时,都需要通知项目经理。

这些任务虽然简单,但日积月累会消耗大量时间,并且容易因人为疏忽而出错。

自动化规则 (Automated Actions) 就是Odoo提供的一个强大工具,允许你定义特定的条件,当这些条件满足时,系统会自动执行预设的操作。它就像一个不知疲倦的智能助手,默默地帮你处理这些重复性任务。

核心价值

引入自动化规则能为企业带来显著的核心价值:

  1. 提高效率:将手动操作自动化,释放人力资源,让他们可以专注于更具创造性和战略性的工作。
  2. 减少人为错误:程序化的执行过程避免了人工操作中可能出现的疏忽、遗漏或错误,确保了数据和流程的一致性与准确性。
  3. 标准化流程:确保关键业务流程按照预设的标准执行,提升了管理的规范性。
  4. 提升响应速度:例如,客户提交服务请求后,系统可以立即自动分配并通知相关人员,提高了客户满意度。
  5. 增强数据完整性:可以自动更新或填充相关字段,确保信息的及时和完整。

二、自动化规则的核心组件拆解

要理解自动化规则如何工作,我们需要了解它的三个核心组成部分:模型 (Model)触发条件 (Trigger)服务器动作 (Action)

让我们通过一个贯穿始终的简单例子来理解这些组件:

例子:当一个新的“联系人 (Contact)”被创建时,系统自动为该联系人记录一条备注,内容为“新联系人由自动化规则创建”。

1. 模型 (Model) - 自动化的“对象”

  • 定义:在Odoo中,模型 (Model) 代表了你业务中的各种数据对象或记录类型。你可以将其理解为你要在哪一类事物上执行自动化。
  • 解释:每一个应用(如销售、采购、库存、CRM等)都由多个模型组成。例如,“销售订单 (Sale Order)”、“产品 (Product)”、“客户发票 (Customer Invoice)”、“潜在客户 (Lead/Opportunity)”、“员工 (Employee)”等等,这些都是模型。自动化规则总是针对某一个特定的模型来设置的。
  • 为何重要:模型定义了自动化的上下文。没有明确的对象,自动化就无从谈起。
  • 对应示例:在我们的例子中,自动化的“对象”是 “联系人 (Contact)”。在Odoo的技术术语中,联系人模型通常是 res.partner

2. 触发条件 (Trigger) - 自动化的“时机”

  • 定义触发条件 (Trigger) 决定了自动化规则何时被激活并执行。它指定了在选定模型上发生什么特定事件时,应该启动自动化。
  • 解释:Odoo提供了多种类型的触发条件,常见的有:
    • 创建时 (On Creation):当一条新的记录被创建时。
    • 更新时 (On Update):当一条已存在的记录被修改并保存时。你可以进一步指定仅当某些特定字段的值发生变化时才触发。
    • 创建和更新时 (On Creation & Update):以上两者的结合。
    • 删除时 (On Deletion):当一条记录被删除时。
    • 基于表单修改 (On Form Modification):当用户在表单视图中修改字段值时(即使还未保存),这对于动态更新界面非常有用。
    • 基于时间条件 (Based on Timed Condition):例如,当某个日期字段达到特定时间点(如“订单计划发货日期”的前一天)时触发。
  • 为何重要:触发条件是自动化的“开关”,确保自动化在正确的时间点执行。
  • 对应示例:在我们的例子中,自动化的“时机”是 “当一个新的联系人被创建时”。所以,这里的触发条件就是 “创建时 (On Creation)”

3. 服务器动作 (Server Action) - 需要执行的“任务”

  • 定义服务器动作 (Action) 定义了当触发条件满足时,系统具体需要执行什么操作。
  • 解释:服务器动作是预定义的一系列操作,可以非常灵活。常见的服务器动作类型包括:
    • 执行Python代码 (Execute Python Code):允许你编写一小段Python脚本来执行复杂逻辑,如更新字段值、创建关联记录、调用模型的方法等。
    • 创建新记录 (Create a new Record):在另一个模型中创建一条新的记录。
    • 更新记录 (Update the Record):修改当前被触发记录的某些字段值。
    • 发送邮件 (Send Email):自动发送一封预设模板的邮件。
    • 添加关注者 (Add Followers):将某些用户添加为记录的关注者,让他们可以收到关于此记录的通知。
    • 执行多个动作 (Execute several actions):将多个单一的服务器动作组合起来按顺序执行。
  • 为何重要:服务器动作是自动化的“执行者”,它完成了你希望系统自动完成的具体工作。
  • 对应示例:在我们的例子中,需要执行的“任务”是 “自动为该联系人记录一条备注,内容为‘新联系人由自动化规则创建’”。这个任务可以通过一个类型为 “执行Python代码 (Execute Python Code)” 的服务器动作来实现。该Python代码会调用Odoo的日志功能,在联系人下方的“沟通栏 (Chatter)”中添加这条备注。

小结:一个自动化规则可以被概括为:“当 模型A (Model) 上的 事件B (Trigger) 发生时,执行 操作C (Server Action)。”

三、工作流程可视化:自动化规则的生命周期

为了更清晰地理解自动化规则的运作流程,我们可以将其生命周期概括为以下几个步骤:

  1. 事件发生 (Event Occurs)
    • 用户在Odoo中执行了一个操作,例如创建了一个新的联系人记录,或者修改了一个已有的销售订单。
    • 示例:用户点击“保存”按钮,创建了一个名为“张三”的新联系人。
  2. 系统监控与匹配 (System Monitors & Matches)
    • Odoo系统持续监控所有模型上的活动。
    • 当一个事件发生时(如记录被创建、更新等),系统会检查是否有与该 模型 (Model) 和该 触发条件 (Trigger) 相匹配的已启用的自动化规则。
    • 示例:系统检测到“联系人 (res.partner)”模型上发生了一个“创建时 (On Creation)”事件。它会查找所有针对“联系人”模型且触发条件为“创建时”的自动化规则。
  3. 条件评估 (Condition Evaluation - 如果有)
    • 许多自动化规则还可以设置额外的“适用条件 (Apply on)”,通常使用Python表达式定义。如果设置了这些条件,系统会先评估这些条件是否为真。
    • 示例:假设我们的规则有一个额外条件:“仅当联系人类型为‘公司’时”。如果新创建的联系人“张三”是个人,则规则不执行。如果我们的简单例子没有这个额外条件,则跳过此步。
  4. 执行服务器动作 (Execute Server Action)
    • 如果触发条件匹配(并且额外条件也满足,如果存在),系统就会执行该自动化规则所关联的 服务器动作 (Server Action)
    • 示例:系统执行预设的服务器动作——运行一小段Python代码,在“张三”这个联系人记录的沟通栏中添加一条备注:“新联系人由自动化规则创建”。
  5. 任务完成 (Task Completed)
    • 自动化操作执行完毕。
    • 示例:备注成功添加。用户或其他相关人员可以在“张三”的联系人详情页面看到这条自动生成的备注。

流程图简化表示:

graph LR
    A[用户操作/系统事件<br/>(如: 创建联系人)] --> B{Odoo系统监控};
    B -- 模型与触发条件匹配? --> C{存在匹配的自动化规则?};
    C -- 是 --> D{评估额外条件(如有)};
    D -- 条件满足 --> E[执行服务器动作<br/>(如: 添加备注)];
    E --> F[自动化任务完成];
    C -- 否 --> G[无操作];
    D -- 条件不满足 --> G;

四、界面导航:如何找到并使用自动化规则

要访问和创建自动化规则,你通常需要激活Odoo的 开发者模式 (Developer Mode)

  1. 激活开发者模式
    • 通常在主菜单的 “设置 (Settings)” 应用中。
    • 在“常规设置 (General Settings)” 页面的右侧或底部,你会找到 “激活开发者模式 (Activate the developer mode)” 或类似的链接。点击它。
    • 激活后,你会在界面上看到一些额外的技术菜单和选项。
  2. 导航到自动化规则菜单
    • 一旦开发者模式激活,导航路径通常是:

设置 (Settings) > 技术 (Technical) > 自动化 (Automation) > 自动化规则 (Automated Actions)

  1. 界面布局简介
    • 列表视图 (List View):当你进入“自动化规则”菜单时,首先看到的是一个列表,显示了系统中所有已创建的自动化规则。这里通常会包含规则的名称 (Name)、关联的模型 (Model)触发条件 (Trigger) 等关键信息。你可以通过筛选器和分组来查找特定的规则。
    • 表单视图 (Form View):点击“创建 (Create)”按钮或列表中的任一规则,会进入表单视图。这是你配置或查看自动化规则详情的地方。你会在这里设置:
      • 名称 (Name):给你的自动化规则起一个描述性的名字。
      • 模型 (Model):选择此规则作用于哪个数据模型。
      • 触发 (Trigger):选择激活此规则的触发条件。
      • (可选) 适用域 (Apply on / Filter):这是一个过滤器,用于进一步限定规则作用的记录范围(例如,仅对特定类型的客户生效)。
      • 动作执行 (Action To Do):选择一个已存在的服务器动作 (Server Action),或者直接在这里配置一个简单的动作。对于复杂逻辑,通常会先在“服务器动作”菜单(设置 > 技术 > 自动化 > 服务器动作)中创建好服务器动作,然后在这里引用。

温馨提示:在生产环境中创建或修改自动化规则时,请务必小心,并最好先在测试环境中进行充分测试,以避免意外的批量操作导致数据问题。

五、一个简单的例子贯穿始终 (回顾)

让我们再次回顾我们一直使用的例子,并将其与Odoo中的设置对应起来:

  • 目标:当一个新的“联系人 (Contact)”被创建时,系统自动为该联系人记录一条备注,内容为“新联系人由自动化规则创建”。
  • 自动化规则配置要点
    1. 名称 (Name): 自动为新联系人添加创建备注 (或者任何你觉得清晰的名称)
    2. 模型 (Model): 联系人 (res.partner)
    3. 触发 (Trigger): 创建时 (On Creation)
    4. 动作执行 (Action To Do): 这里你需要选择或创建一个服务器动作 (Server Action)
      • 该服务器动作的类型会是 “执行Python代码 (Execute Python Code)”
      • Python代码大致会是(仅为示意,实际代码可能因Odoo版本略有差异):
# record 代表当前被操作的联系人记录
record.message_post(body="新联系人由自动化规则创建")

当这些都设置好并保存后,每当你系统中的任何用户(或通过导入等方式)创建了一个新的联系人,这条备注就会自动出现在该联系人的沟通栏中。

六、学习自动化规则的前置知识要求

虽然本指南力求简单,但要更深入地理解和有效使用Odoo的自动化规则,具备以下基础知识会非常有帮助:

  1. Odoo基本操作与导航:熟悉如何在Odoo界面中进行浏览、查找菜单、创建和编辑记录。
  2. Odoo模型概念 (Model Concept):这是最核心的一点。你需要理解Odoo中的数据是如何组织的,什么是“联系人”、“销售订单”、“产品”等。知道这些模型代表了什么业务对象,以及它们可能包含哪些信息(字段)。不一定需要知道它们的技术名称(如res.partner),但至少要理解其业务含义。
  3. (可选,但推荐) Odoo字段 (Fields) 的基本认识:了解模型是由不同的字段(如联系人模型的“名称”、“电话”、“邮箱”字段)组成的。这对于设置更复杂的触发条件(如“当某个字段的值发生变化时”)或服务器动作(如“更新某个字段的值”)至关重要。
  4. (进阶) Python基础知识:如果你希望创建更复杂的服务器动作(尤其是“执行Python代码”类型),那么懂一点Python会让你如虎添翼。但对于许多常见的自动化需求,通过预设的服务器动作类型也能实现。
  5. (进阶) Odoo域表达式 (Domain Syntax):当设置“适用域 (Apply on / Filter)”或在服务器动作中需要筛选记录时,会用到Odoo的域表达式。这是一种特定的查询语法。

对于初学者,首先掌握模型概念,并尝试使用简单的触发条件(如“创建时”)和简单的服务器动作(如“更新记录”中的固定值更新),是很好的起点。

七、总结与展望

Odoo的自动化规则 (Automated Actions) 是一个强大而灵活的工具,它通过将模型 (Model) 上的特定触发条件 (Trigger) 与预定义的服务器动作 (Action) 相关联,实现了业务流程的自动化。

通过有效地使用自动化规则,企业可以:

  • 显著提升运营效率
  • 降低人为错误的风险。
  • 确保业务流程的标准化和一致性

虽然初看起来“技术 (Technical)”菜单下的选项可能有些令人生畏,但一旦你理解了其核心组件和工作原理,就会发现它为你打开了一扇通往更高效、更智能工作方式的大门。

建议你从简单的自动化任务开始实践,逐步探索其更高级的功能。例如,当一个潜在客户被标记为“合格”时,自动创建一个跟进任务;或者当一个项目任务逾期时,自动发送提醒邮件给负责人。可能性是无穷的!

希望这份入门指南能帮助你迈出探索Odoo自动化规则的第一步。祝你在Odoo的学习和使用过程中一切顺利!


二、自动化规则触发条件 (Triggers) 深度解析

Odoo的自动化规则是实现业务流程自动化的核心功能之一。其强大之处不仅在于可执行的动作多样,更在于其灵活的触发条件 (Triggers) 定义。正确理解和选用触发条件,是确保自动化规则按预期运行、提升系统效率并避免不必要性能开销的关键。


一、触发条件 (Triggers) 分类与解析

Odoo的自动化规则提供了多种触发条件,以适应不同的业务事件和时间节点。以下是各类触发条件的详细解析:

1. 创建时 (On Creation)
  • 技术名称: on_create
  • 解析: 当在指定模型中成功创建一个新的记录并保存到数据库后,此触发条件被激活。这是最直接的触发方式之一,用于在新数据产生时立即执行某些初始化或关联操作。
  • 执行时机: 在记录成功写入数据库 create() 方法执行完毕后。
2. 更新时 (On Update)
  • 技术名称: on_write
  • 解析: 当一个已存在的记录被修改并成功保存到数据库后,此触发条件被激活。这是非常常用的一种触发器,用于响应数据的变化。为了精确控制和优化性能,此触发器有几个关键参数需要重点关注(详见第三部分)。
  • 执行时机: 在记录成功写入数据库 write() 方法执行完毕后。
3. 创建与更新时 (On Creation & Update)
  • 技术名称: on_create_or_write
  • 解析: 这是“创建时”和“更新时”两种触发条件的组合。无论记录是新创建的还是被修改,只要数据成功保存到数据库,此触发条件都会被激活。它适用于那些在记录生命周期的任何数据变更点都需要执行的逻辑。
  • 执行时机: create()write() 方法执行完毕后。
4. 删除时 (On Deletion)
  • 技术名称: on_unlink
  • 解析: 当一个或多个记录从数据库中被删除前,此触发条件被激活。这允许在数据被永久移除前执行一些清理、归档或通知操作。需要注意的是,动作执行时记录尚未完全删除,因此仍可访问其数据。
  • 执行时机: 在 unlink() 方法执行过程中,删除数据库记录之前。
5. 基于表单修改 (On Form Modification)
  • 技术名称: on_change
  • 解析: 这是一种客户端与服务器端交互的触发机制。当用户在Odoo的表单视图中修改了某个字段的值,并且该字段被配置为会触发 on_change 事件时,此自动化规则被激活。它不一定等待记录保存,而是即时响应用户的界面操作。这使得它可以动态改变表单上其他字段的值、设置域(domain)或显示警告。
    • 注意: 虽然自动化规则的 on_change 是服务器端行为,但它是由客户端的字段变化事件触发的。它主要用于在用户交互过程中提供即时反馈和计算,而不是在数据持久化时执行后台逻辑。
  • 执行时机: 用户在表单界面修改了监听字段的值后,焦点离开该字段或特定UI事件发生时,在数据发送到服务器处理 onchange 逻辑时。
6. 基于时间条件 (Based on Timed Condition)
  • 技术名称: on_time
  • 解析: 此触发条件允许自动化规则在特定时间点或相对于记录中某个日期/时间字段的某个时间点被激活。它依赖Odoo的计划任务 (Scheduled Actions / Cron Jobs) 来定期检查满足条件的记录。
  • 执行时机: 由系统后台的计划任务根据设定的日期字段和延迟周期性检查并触发。

二、应用场景分析

为每一种触发条件提供至少两个具体的业务应用场景,以助理解:

1. 创建时 (On Creation)
  • 场景一 (CRM): 当一个新的潜在客户 (Lead/Opportunity) 被创建时,自动根据其来源地或行业,将其分配给相应的销售团队或销售人员。
    • 模型: 潜在客户 (crm.lead)
    • 动作: 更新记录 (设置销售团队字段 team_id 或销售员字段 user_id)
  • 场景二 (HR): 当一个新的员工 (Employee) 记录被创建时,自动为其创建一套标准的入职清单任务 (e.g., 签订合同、领取设备、账户开通)。
    • 模型: 员工 (hr.employee)
    • 动作: 创建新记录 (在任务模型 project.task 中为该员工创建多个任务)
2. 更新时 (On Update)
  • 场景一 (销售): 当销售订单 (Sale Order) 的“状态”字段 (state) 从“报价 (Quotation)”更新为“销售订单 (Sales Order / Confirmed)”时,自动创建相关的发货单 (Delivery Order)。
    • 模型: 销售订单 (sale.order)
    • 触发字段: 状态 (state)
    • 前置条件过滤: 旧状态为 'sent' (报价已发送) 或 'draft' (草稿报价)
    • 后置条件过滤: 新状态为 'sale' (销售订单)
    • 动作: 执行Python代码 (调用创建发货单的方法)
  • 场景二 (项目管理): 当项目任务 (Project Task) 的“阶段 (Stage)”字段 (stage_id) 更新为“已完成 (Done)”时,自动发送一封邮件通知项目经理和客户。
    • 模型: 项目任务 (project.task)
    • 触发字段: 阶段 (stage_id)
    • 后置条件过滤: 阶段标记为已完成 (is_closed 为 True)
    • 动作: 发送邮件
3. 创建与更新时 (On Creation & Update)
  • 场景一 (库存/产品): 当一个产品 (Product) 的“成本价 (Cost Price)”字段 (standard_price) 被首次设置或后续被修改时,自动根据预设的利润率 (margin) 重新计算并更新其“销售价 (Sales Price)” (list_price)。
    • 模型: 产品 (product.template / product.product)
    • 触发字段: 成本价 (standard_price)
    • 动作: 执行Python代码 (获取成本价,计算并更新销售价)
  • 场景二 (会计): 当一张客户发票 (Customer Invoice) 的“付款状态 (Payment State)”发生变化时(例如,从“未支付”到“已支付”或“部分支付”),自动记录一条内部备注说明变化详情和时间。
    • 模型: 发票 (account.move)
    • 触发字段: 付款状态 (payment_state)
    • 动作: 执行Python代码 (使用 message_post 添加备注)
4. 删除时 (On Deletion)
  • 场景一 (订阅管理): 当一个订阅合同 (Subscription Contract) 被删除(通常意味着客户取消订阅)时,自动记录一条取消日志,包含客户信息、取消时间及可能的原因(如果能获取)。
    • 模型: 订阅 (sale.subscription)
    • 动作: 创建新记录 (在自定义的“取消日志”模型中记录信息)
  • 场景二 (文档管理): 当一个标记为“机密”的文档 (Document) 被删除时,自动向系统管理员发送一条通知邮件,以便审计或确认操作的合规性。
    • 模型: 文档 (ir.attachment 或自定义文档模型)
    • 后置条件过滤: 文档有机密标记
    • 动作: 发送邮件
5. 基于表单修改 (On Form Modification)
  • 场景一 (销售/采购): 在创建销售订单行 (Sale Order Line) 或采购订单行时,当用户选择了某个“产品 (Product)”后,自动填充该产品的默认“描述 (Description)”和“单位 (Unit of Measure)”。
    • *模型: <em>销售订单行 (sale.order.line)</em>
    • 监听字段: 产品 (product_id)
    • 动作: 返回需更新字段的值 (description, product_uom)
  • 场景二 (制造): 在创建制造单 (Manufacturing Order) 时,当用户修改了“计划生产数量 (Quantity to Produce)”后,系统动态地重新计算并显示预估的“原材料需求量 (Material Requirements)”(可能通过更新一个文本字段或只读的one2many字段的domain)。
    • 模型: 制造单 (mrp.production)
    • 监听字段: 数量 (product_qty)
    • 动作: 返回需更新字段的值或应用的域 (domain)
6. 基于时间条件 (Based on Timed Condition)
  • 场景一 (CRM/任务管理): 对于所有“截止日期 (Deadline)”在未来24小时内,且状态仍为“进行中 (In Progress)”的任务 (Task)潜在客户活动 (CRM Activity),自动向负责人发送提醒邮件。
    • 模型: 项目任务 (project.task) 或邮件活动 (mail.activity)
    • 日期字段: 截止日期 (date_deadline)
    • 延迟设置: -1 天 (即提前一天检查)
    • 后置条件过滤: 状态为 'in_progress' 或未完成
    • 动作: 发送邮件
  • 场景二 (订阅/合同管理): 对于所有“到期日期 (End Date)”在未来30天内的订阅合同 (Subscription Contract)服务合同 (Service Contract),自动创建一个续约提醒的活动或任务,并分配给客户经理。
    • 模型: 订阅 (sale.subscription)
    • 日期字段: 结束日期 (date)
    • 延迟设置: -30 天
    • 后置条件过滤: 状态为 'running' 或未关闭
    • 动作: 创建新记录 (创建 mail.activity 或 project.task)

三、关键参数说明

1. “更新时 (On Update)”触发器的关键参数

当选择“更新时”作为触发条件,以下参数对于精确控制和性能至关重要:

  • 触发字段 (Triggering Fields / filter_field_ids):
    • 用法: 这是一个可选参数。你可以指定一个或多个字段。如果设置了此参数,则只有当这些被指定的字段之一发生变化时,自动化规则才会被触发。如果不设置,则该记录的任何字段发生变化并保存都会触发规则。
    • 重要性:
      • 性能: 明确指定触发字段可以显著提升性能,避免不必要的规则执行。例如,如果只关心订单状态变化,就不应在订单备注修改时也执行一遍检查。
      • 精确性: 确保自动化逻辑只在真正相关的上下文改变时执行。
  • 前置条件过滤 (Before Update Domain / filter_pre_domain):
    • 用法: 这是一个域表达式 (Domain),用于在记录更新之前检查其状态。只有当记录在更新前的状态满足此域条件时,自动化规则才可能被触发(还需结合其他条件如触发字段和后置条件)。
    • 重要性:
      • 状态转换判断: 非常适合用于判断字段值从某个旧值变为新值的场景。例如,要判断订单状态从“报价”变为“已确认”,filter_pre_domain 可以设置为 [('state', '=', 'sent')],而后置条件 filter_domain 设置为 [('state', '=', 'sale')]
      • 避免无效触发: 如果记录更新前就不满足某些基础条件,则无需进一步处理。
  • 后置条件过滤 (Apply on / Filter / filter_domain):
    • 用法: 这也是一个域表达式,用于在记录更新之后(数据已写入数据库)检查其状态。只有当记录更新后的状态满足此域条件时,自动化规则才会最终执行其服务器动作。
    • 重要性:
      • 最终状态确认: 确保自动化操作仅在记录达到期望的最终状态时执行。
      • 组合条件: 与 filter_pre_domainfilter_field_ids 结合使用,可以构建非常精确的触发逻辑。
2. “基于时间条件 (Based on Timed Condition)”触发器的关键参数
  • 触发日期字段 (Trigger Date Field / trigger_field_ids - 应为单一日期/时间字段):
    • 用法: 指定模型中的一个日期 (Date) 或日期时间 (Datetime) 字段,该字段的值将作为计算触发时间的基准。
    • 重要性: 这是时间条件的核心,所有延迟计算都围绕此字段进行。例如,“任务截止日期”、“合同到期日”、“下次活动日期”等。
  • 延迟及单位 (Delay After Trigger Date / filter_delayfilter_delay_type):
    • 用法:
      • filter_delay: 一个整数,表示延迟的数量。
      • filter_delay_type: 延迟的单位,如 days (天), hours (小时), weeks (周), months (月)。
    • 设置:
      • 正数延迟: 表示在“触发日期字段”的值之后的某个时间点触发(例如,date_start + 5 days)。
      • 负数延迟: 表示在“触发日期字段”的值之前的某个时间点触发(例如,date_deadline - 3 days,表示截止日期前3天)。
      • 零延迟: 表示就在“触发日期字段”当天触发(具体时间取决于计划任务的执行频率)。
    • 重要性: 提供了极大的灵活性,可以精确安排在事件发生前、发生时或发生后的某个特定时间点执行操作。例如,提前发送提醒、到期日自动关闭等。

四、对比与选择

下面是一个表格,横向对比不同触发条件的特性,帮助您根据需求做出最佳选择:

特性

创建时 (On Creation)

更新时 (On Update)

创建与更新时 (On Creation & Update)

删除时 (On Deletion)

基于表单修改 (On Form Modification)

基于时间条件 (Based on Timed Condition)

触发事件

新记录保存

已有记录修改并保存

新记录或已有记录保存

记录从数据库删除前

用户在表单修改字段值(未必需保存)

达到预设的日期/时间条件

触发频率

低 (每次创建)

中/高 (每次有效更新)

中/高 (每次创建或有效更新)

低 (每次删除)

高 (用户界面交互)

周期性 (由Cron决定)

性能影响

较低

中 (取决于触发字段和过滤条件)

中/高 (同上)

较低

可能高 (影响UI响应)

中 (取决于检查记录数和Cron频率)

主要适用场景

初始化、默认值设置、关联记录创建

状态变更、关键数据更新响应、数据校验

数据一致性维护、日志记录、通用计算逻辑

清理、归档、通知

动态表单、即时计算、用户引导

提醒、到期处理、周期性任务

关键参数

N/A

触发字段, 前/后置过滤

同“更新时”

N/A

监听字段 (在模型字段定义中)

日期字段, 延迟时间/单位

数据状态

已创建

更新前/更新后均可检查

同上

删除前仍可访问

可能是临时未保存状态

满足时间条件的记录

执行上下文

服务器端

服务器端

服务器端

服务器端

服务器端 (由客户端触发)

服务器端 (由计划任务触发)


五、性能影响与建议

不当的自动化规则配置可能对系统性能产生负面影响。以下是一些建议:

  1. 精确化“更新时”触发:
    • 始终指定“触发字段 (Triggering Fields)”:除非绝对必要,否则不要让规则在任何字段更新时都触发。这能极大减少不必要的执行。
    • 善用“前置/后置条件过滤 (Domains)”:通过filter_pre_domainfilter_domain尽早排除不相关的记录,减少进入服务器动作的记录数量。
  2. 优化“基于表单修改”:
    • on_change 方法执行的逻辑应尽可能轻量。复杂的计算或数据库查询会直接影响用户界面的响应速度,导致卡顿。
    • 避免在 on_change 中执行会修改大量其他记录或触发连锁反应的操作。
  3. 审慎使用“创建与更新时”:
    • 由于它在创建和更新时都会触发,确保其执行的动作确实是两种场景都必需的,并且已通过触发字段和过滤条件优化。
  4. “基于时间条件”的优化:
    • 合理设置计划任务 (Cron Job) 的执行频率。过于频繁的检查对于变化不多的数据模型是浪费资源。
    • 确保用于筛选记录的域 (filter_domain) 和日期比较是高效的,最好能利用数据库索引。
  5. 服务器动作 (Server Action) 的代码效率:
    • 自动化规则执行的Python代码应高效。避免循环中的多次数据库查询 (N+1问题),使用Odoo ORM的批量操作方法。
    • 对于耗时较长的操作,考虑使用队列任务 (e.g., queue_job 模块) 将其异步化,避免阻塞主流程。
  6. 测试,测试,再测试:
    • 在开发或测试环境中充分测试自动化规则,尤其是在有大量数据的情况下,观察其行为和性能影响。
    • 检查日志,确保规则按预期触发,并且没有意外的副作用。
  7. 监控与审计:
    • Odoo的日志系统可以帮助追踪自动化规则的执行。定期审查日志,特别是在系统负载较高时,有助于发现潜在的性能瓶颈。

六、总结与展望

Odoo的自动化规则触发条件提供了多样化的选择,以应对企业运营中层出不穷的自动化需求。从即时响应用户操作的 On Form Modification,到数据持久化时的 On CreationOn Update,再到基于特定时间点的 On Timed Condition,每一种触发器都有其独特的价值和适用场景。

作为系统架构师或资深顾问,深刻理解这些触发条件的内在机制、关键参数和性能考量,是设计出健壮、高效且易于维护的Odoo解决方案的基石。通过明智地组合这些触发器与服务器动作,您可以将Odoo打造成一个真正智能的业务自动化平台,极大地提升运营效率,降低人为错误,并使企业能够更敏捷地响应市场变化。


三、“服务器动作 (Server Actions)”详尽分析

服务器动作 (Server Actions) 是Odoo中一个极其强大和灵活的工具,允许用户通过配置或少量代码来定义和执行各种后台操作。它们是Odoo自动化生态系统的核心组成部分,经常与“自动化规则 (Automated Actions)”结合使用,以响应特定的触发条件。同时,服务器动作也可以独立应用于计划任务、按钮点击(通过ir.actions.server)、或作为上下文菜单项,为用户提供了从“无代码”、“低代码”到“专业代码”不同层次的定制能力。

理解并熟练运用服务器动作,能够极大地提升业务流程的自动化水平、数据处理能力和系统的整体效率。

导航至服务器动作

要创建和管理服务器动作,您首先需要激活Odoo的 开发者模式

  1. 激活开发者模式:前往 设置 (Settings) 应用,在“常规设置 (General Settings)”页面的底部(或根据Odoo版本在用户菜单下),点击“激活开发者模式 (Activate the developer mode)”。
  2. 访问服务器动作菜单:激活开发者模式后,导航路径为:

设置 (Settings) > 技术 (Technical) >操作 (action) > 服务器动作 (Server Actions)

进入后,您会看到现有服务器动作的列表视图。点击“创建 (Create)”按钮或选择一个现有动作即可进入其表单视图进行配置。

服务器动作类型全览与配置详解

Odoo提供了多种类型的服务器动作,每种都有其特定的用途和配置选项。

A. 执行Python代码 (Execute Python Code)

这是最强大和灵活的服务器动作类型,允许您执行任意Python代码片段来与Odoo环境交互。

  • 定义与用途: 当内置的动作类型无法满足复杂业务逻辑时,可通过编写Python脚本实现高度定制化的操作,如复杂的计算、条件逻辑、调用模型方法、与其他系统集成等。
  • 配置UI与步骤:
    1. 在服务器动作表单视图中,设置 名称 (Name),例如:“计算客户信用积分”。
    2. 选择此动作主要关联的 模型 (Model) (可选,但推荐,以便recordrecords变量能正确关联)。
    3. 在“动作类型 (Action Type)”字段中,选择“执行Python代码 (Execute Python Code)”。
    4. 下方会显示一个名为“Python代码 (Python Code)”的多行文本框。在此处编写您的Python脚本。
UI描述:
- 名称 (Name): [文本输入框,例如:"更新项目任务优先级"]
- 模型 (Model): [模型选择器,例如:"项目任务 (project.task)"]
- 动作类型 (Action Type): [下拉选择框,选中 "执行Python代码"]
-- Python代码区域 --
Python代码:
"""在此处输入您的Python代码。
可用的变量:
- env: Odoo Environment auf Odoo Environment (env)
- model: ORM model of the record on which the action is triggered (model)
- record: record on which the action is triggered (if action is run on a single record)
- records: recordset of all records on which the action is triggered (if action is run on multiple records)
- time, datetime, dateutil, timezone: useful Python libraries
- log(message, level='info'): function to log debug information in Odoo server logs
- Warning(message): Warning Exception to display an error message in the UI.
"""
# 示例代码 (将在下面详细解释)
  • 关键可用变量与函数:
    • env: Odoo环境对象,是所有ORM操作的入口。例如:env['res.partner'] 可以访问“联系人”模型。
    • model: 当前服务器动作表单中“模型”字段所选模型的ORM模型对象。例如,如果模型是 project.task,则 model 就是 env['project.task'].
    • record: 当动作在单个记录上执行时(例如,自动化规则针对单个记录触发),record 代表该条记录的浏览对象。
    • records: 当动作在多个记录上执行时(例如,在列表视图中选择多条记录并执行上下文动作),records 代表这些记录的记录集 (RecordSet)。通常情况下,即便只有一条记录,records 也会被填充。建议优先使用 records 并按需迭代或取其一。
    • time, datetime, dateutil, timezone: Python标准的时间和日期处理库。
    • log(message, level='info'): Odoo提供的日志函数,可将信息输出到服务器日志。level 可以是 debug, info, warning, error, critical
    • Warning(message): 抛出一个用户可见的警告/错误对话框。例如:raise Warning("该操作无法完成,因为条件不满足!")
    • UserError(message): 类似于Warning, 用于业务逻辑错误提示。
  • 简单代码示例:

假设有一个在“销售订单 (sale.order)”模型上的动作,当订单总额超过5000时,自动标记为“大额订单” (自定义字段 x_is_large_order 类型为布尔型)。

# 检查是否有记录,并且记录集非空
if records and records.exists():
    for order in records:
        if hasattr(order, 'x_is_large_order'): # 确保字段存在
            if order.amount_total > 5000:
                order.x_is_large_order = True
                log(f"订单 {order.name} (ID: {order.id}) 已标记为大额订单。总额: {order.amount_total}", level='info')
            else:
                order.x_is_large_order = False # 如果需要,也可以处理不满足条件的情况
        else:
            log(f"订单 {order.name} (ID: {order.id}) 上不存在字段 x_is_large_order。", level='warning')
else:
    log("Python服务器动作执行,但没有有效的记录上下文。", level='info')
  • 安全风险与注意事项:
    • 高风险: “执行Python代码”赋予了执行任意服务器端代码的能力,这可能导致数据损坏、安全漏洞或系统不稳定。只有绝对可信的管理员用户才应拥有创建和修改此类服务器动作的权限。
    • 代码审查: 所有Python代码都应经过严格审查。
    • 性能: 避免在代码中执行低效的循环或大量数据库查询。
    • 错误处理: 使用 try...except 块来捕获潜在错误,并使用 log()Warning() 提供反馈。
  • 应用场景: 实现复杂的自定义业务逻辑,例如:根据多个条件动态计算佣金、在特定数据更新后调用外部API、执行复杂的验证规则等。
B. 创建新记录 (Create a new Record)

此动作类型用于在指定的模型中创建一条或多条新的记录。

  • 定义与用途: 自动化数据录入,例如当某个事件发生时,自动在另一个相关的业务对象中创建一条新记录。
  • 配置UI与步骤:
    1. 设置 名称 (Name) 和可选的 模型 (Model) (当前动作关联的模型)。
    2. 动作类型 (Action Type): 选择“创建新记录 (Create a new Record)”。
    3. 目标模型 (Create/Write Target Model): 选择您希望在哪个模型中创建新记录。例如,如果您想创建一个新任务,就选择“任务 (project.task)”。
    4. 在“字段 (Fields)”标签页下(或直接在主界面,根据Odoo版本),您会看到一个用于定义新记录字段值的列表。点击“添加一行 (Add a line)”:
      • 字段 (Field): 从“目标模型”中选择一个要赋值的字段。
      • 类型 (Type):
        • 值 (Value): 直接输入一个固定的值。
        • 表达式 (Expression): 输入一个Python表达式,其结果将作为字段的值。
      • 值 / Python表达式 (Value / Python Expression): 根据所选类型输入具体内容。
UI描述 (字段映射部分):
字段列表:
| 字段 (Field)       | 类型 (Type)        | 值 / Python表达式 (Value / Python Expression) |
|--------------------|--------------------|------------------------------------------------|
| [选择目标字段]     | [值/表达式]        | [输入值或Python表达式]                           |
| ...                | ...                | ...                                            |
  • 关键概念:
    • 字段映射: 核心在于正确地将源数据(通常来自recordrecords,即触发此动作的记录)或固定值映射到新记录的相应字段。
    • 使用表达式:
      • 引用当前记录的字段: record.name (如果动作由单个记录触发)
      • 引用当前记录的ID (用于关联关系): record.id (例如,在新任务的project_id字段中填入当前项目的ID)
      • 日期/时间: datetime.datetime.now() (当前时间), (datetime.date.today() + dateutil.relativedelta.relativedelta(days=5)) (5天后)
  • 应用场景: 当一个“销售订单 (Sale Order)”被确认时,自动为该订单在“项目 (Project)”模型中创建一个新项目,并将订单号、客户等信息传递过去。
    • 目标模型: 项目 (project.project)
    • 字段映射示例:
      • name (项目名称) (类型: 表达式) -> f"项目 - {record.name}" (假设record是销售订单)
      • partner_id (客户) (类型: 表达式) -> record.partner_id.id
      • sale_order_id (关联销售订单) (类型: 表达式) -> record.id
C. 更新记录 (Update the Record)

此动作用于修改触发此动作的当前记录的一个或多个字段值。

  • 定义与用途: 基于某些条件或事件,自动更新当前记录的信息。
  • 配置UI与步骤:
    1. 设置 名称 (Name)模型 (Model) (动作将作用于此模型的记录)。
    2. 动作类型 (Action Type): 选择“更新记录 (Update the Record)”。
    3. 在“字段 (Fields)”标签页下(或主界面),配置要更新的字段及其新值,与“创建新记录”中的字段映射方式类似:
      • 字段 (Field): 从当前“模型”中选择一个要更新的字段。
      • 类型 (Type): “值 (Value)”或“表达式 (Expression)”。
      • 值 / Python表达式 (Value / Python Expression): 输入具体内容。
  • 应用场景: 当一个“潜在客户 (Lead)”的阶段更新为“合格”时,自动将其“优先级 (Priority)”字段(通常是选择字段)更新为“高(3星)”。
    • 模型: 潜在客户/商机 (crm.lead)
    • 字段映射示例:
      • priority (优先级) (类型: 值) -> 3 (假设 '3' 代表高优先级)
D. 执行多个动作 (Execute several actions)

允许将多个独立的服务器动作串联起来,按顺序执行。

  • 定义与用途: 构建更复杂的自动化流程,通过组合简单的、可复用的原子动作来实现多步骤操作。
  • 配置UI与步骤:
    1. 设置 名称 (Name) 和可选的 模型 (Model)
    2. 动作类型 (Action Type): 选择“执行多个动作 (Execute several actions)”。
    3. 在“动作 (Actions)”标签页下,点击“添加一行 (Add a line)”。
    4. 在弹出的选择框中,选择一个已存在的服务器动作加入到执行序列中。可以添加多个。
UI描述 (子动作列表部分):
子动作列表:
| 序号 | 服务器动作 (Server Action to Run)             |
|------|----------------------------------------------|
| 1    | [选择一个已存在的服务器动作,例如:"更新记录状态"] |
| 2    | [选择另一个已存在的服务器动作,例如:"发送通知邮件"]|
| ...  | ...                                          |
    • 执行顺序: 通常按照在列表中添加的顺序执行。
  • 应用场景: (详见下一节“组合应用”)
E. 发送邮件 (Send Email)

此动作用于自动发送一封基于预定义邮件模板的电子邮件。

  • 定义与用途: 在特定事件发生时,自动向相关方(客户、员工等)发送通知、确认或营销邮件。
  • 配置UI与步骤:
    1. 设置 名称 (Name)模型 (Model) (邮件模板通常会使用此模型的记录作为object变量)。
    2. 动作类型 (Action Type): 选择“发送邮件 (Send Email)”。
    3. 邮件模板 (Email Template): 选择一个预先在“设置 > 技术 > 邮件 > 邮件模板 (Settings > Technical > Email > Email Templates)”中创建好的邮件模板 (mail.template)。
    4. 发送模式 (Send Email As): (此选项在某些Odoo版本或上下文中可能不直接显示在服务器动作配置里,更多是邮件模板本身的设置,或在特定按钮动作上下文时出现)
      • 通常服务器动作触发的邮件会直接发送。如果通过按钮触发,可能会有选项是“直接发送”或“打开编辑器”。
  • 关键概念:
    • 邮件模板: 邮件模板是核心,它定义了邮件的主题、收件人(可动态)、正文内容(支持HTML和QWeb表达式,如 object.name 来引用当前记录的名称)、附件等。
  • 应用场景: 当一个“支持请求单 (Helpdesk Ticket)”的状态变为“已解决”时,自动向提交请求的客户发送一封包含解决方案摘要和满意度调查链接的邮件。
F. 添加关注者 (Add Followers)

将指定的用户或合作伙伴添加为当前记录的关注者,使其能够接收到关于此记录的通知。

  • 定义与用途: 确保相关人员能够及时了解到记录的重要更新或讨论。
  • 配置UI与步骤:
    1. 设置 名称 (Name)模型 (Model)
    2. 动作类型 (Action Type): 选择“添加关注者 (Add Followers)”。
    3. 要添加的关注者 (Followers to Add): 点击“添加一行 (Add a line)”,选择一个或多个“合作伙伴 (res.partner)”记录(用户也是一种合作伙伴)。
    4. 默认订阅类型 (Default Subtypes): (可选)选择这些新关注者默认订阅的通知类型(例如,评论、活动安排等)。通常会全选或根据业务需要选择。
  • 应用场景: 当一个“项目 (Project)”创建时,自动将项目经理和销售负责人添加为该项目的关注者。
G. 创建下一个活动 (Create Next Activity)

在当前记录上安排一个计划活动(如待办事项、会议、邮件、电话等)。

  • 定义与用途: 驱动工作流,确保任务得到跟进,是Odoo CRM和项目管理等模块的核心功能。
  • 配置UI与步骤:
    1. 设置 名称 (Name)模型 (Model)
    2. 动作类型 (Action Type): 选择“创建下一个活动 (Create Next Activity)”。
    3. 活动类型 (Activity Type): 选择一个预定义的活动类型(例如“邮件”、“电话”、“待办事项”)。这些在“设置 > 技术 > 讨论 > 活动类型 (Settings > Technical > Discuss > Activity Types)”中配置。
    4. 摘要 (Summary): 活动的简短标题。可使用Python表达式动态生成。
    5. 负责人 (Assigned to): 选择活动的负责人。可以是特定用户,也可以通过表达式(如 record.user_id.id 来分配给记录的负责人)。
    6. 截止日期 (Due Date In): 设置活动的到期时间,例如“5 天后”、“1 小时后”等。
    7. 备注 (Note): 活动的详细描述,支持HTML。
  • 应用场景: 如果一个“潜在客户 (Lead)”超过7天没有任何交互(可以通过自动化规则的日期条件判断),则自动为其负责人创建一个“电话跟进”的活动,截止日期为3天后。
    • 活动类型: 电话 (Phonecall)
    • 摘要 (表达式): f"电话跟进潜在客户: {record.name}"
    • 负责人 (表达式): record.user_id.id
    • 截止日期: 3 天 (Days)

组合应用:“执行多个动作”的威力

“执行多个动作 (Execute several actions)”类型的强大之处在于其模块化流程编排能力。您可以创建一系列简单、单一职责的服务器动作,然后将它们组合起来,形成一个复杂的、多步骤的业务流程。

示例:当“支持请求单 (Helpdesk Ticket)”的状态更新为“已解决 (Solved)”时,执行以下操作:

  1. 原子动作1: 更新解决日期
    • 名称: SA - 更新工单解决日期
    • 模型: 支持请求单 (helpdesk.ticket)
    • 类型: 更新记录 (Update the Record)
    • 字段映射: resolution_date (解决日期) (类型: 表达式) -> datetime.date.today()
  2. 原子动作2: 发送解决通知邮件
    • 名称: SA - 发送工单解决通知
    • 模型: 支持请求单 (helpdesk.ticket)
    • 类型: 发送邮件 (Send Email)
    • 邮件模板: (选择一个预设的“工单已解决”模板)
  3. 原子动作3: 安排回访活动
    • 名称: SA - 安排工单满意度回访
    • 模型: 支持请求单 (helpdesk.ticket)
    • 类型: 创建下一个活动 (Create Next Activity)
    • 活动类型: 电话 (Phonecall)
    • 摘要 (表达式): f"客户满意度回访: {record.name}"
    • 负责人 (表达式): record.user_id.id
    • 截止日期: 2 天 (Days)
  4. 组合动作: 处理已解决工单
    • 名称: SA - 流程:处理已解决工单
    • 模型: 支持请求单 (helpdesk.ticket)
    • 类型: 执行多个动作 (Execute several actions)
    • 子动作:
      1. SA - 更新工单解决日期
      2. SA - 发送工单解决通知
      3. SA - 安排工单满意度回访

应用: 这个“SA - 流程:处理已解决工单”服务器动作可以被一个自动化规则调用,该自动化规则的触发条件是:当“支持请求单”的“阶段 (Stage)”字段更新到某个代表“已解决”的阶段时。

优势:

  • 清晰性: 每个原子动作职责单一,易于理解和维护。
  • 复用性: 原子动作可以在其他组合动作或自动化规则中被复用。
  • 灵活性: 调整流程时,只需修改组合动作中的子动作顺序或替换某个子动作,而无需触及其他部分。

最佳实践与技巧

  • 清晰命名: 为服务器动作起一个描述性强、易于理解的名称,最好能体现其作用和关联模型,例如 SA Lead - Assign High Priority
  • 模块化设计: 优先创建小而专注的原子动作,再通过“执行多个动作”进行组合。
  • 善用表达式:
    • 动态值: Python表达式使得字段赋值非常灵活。例如,基于条件设置不同值:(record.order_line and sum(record.order_line.mapped('price_subtotal'))) or 0.0
    • 日期计算: datetime.date.today(), dateutil.relativedelta.relativedelta(days=X, months=Y)
    • 访问关联对象: record.partner_id.name (获取客户名称), record.user_id.email (获取负责人邮箱)
  • 避免无限循环:
    • 当一个服务器动作(特别是“更新记录”或“执行Python代码”)修改的字段同时也是触发该动作的自动化规则的“触发字段 (Trigger Fields)”时,极易产生无限循环。
    • 对策:
      • 在自动化规则中,尽可能精确地设置“触发字段”。
      • 在“更新时 (On Update)”的自动化规则中,明智地使用“前置条件过滤 (Before Update Domain)”和“应用域 (Apply on / Filter)”来确保只有在真正需要时才执行。
      • 在Python代码中设置“哨兵”字段或检查条件,确保动作只执行一次。例如,更新一个自定义的布尔字段 x_processed_by_sa,并在代码开头检查此字段。
  • Python代码安全: 重申,只有完全受信任的管理员才能编辑包含Python代码的服务器动作。定期审查和测试代码。
  • 理解执行上下文:
    • 明确 recordrecords 的含义。当动作由自动化规则(针对单个记录)触发时,record 是当前记录,records 是包含单个记录的记录集。若从列表视图选择多条记录执行,records 则是这些记录的集合。
    • Python代码中的 model 变量是服务器动作定义中指定的模型,而非 records.model (后者是实际记录的模型名称字符串)。
  • 日志与调试:
    • 在“执行Python代码”时,使用 log() 函数输出调试信息到服务器日志中,便于追踪问题。
    • 复杂逻辑先在Odoo Shell (odoo shell) 中测试。
  • 错误处理:
    • 在Python代码中使用 try...except 捕获预期错误。
    • 使用 Warning(message)UserError(message) 向用户抛出清晰的业务错误提示。
  • 权限考量: 服务器动作通常以触发它的用户的权限运行。如果是通过自动化规则(且由系统cron触发)或计划任务执行,则可能以管理员(uid=OdooBot 或特定用户)权限运行。这会影响数据的可见性和可执行操作。
  • 事务管理: 每个服务器动作(或“执行多个动作”中的每个子动作)通常在自己的事务中运行。如果一个子动作失败,它之前已成功提交的子动作不会自动回滚(除非整个父级操作被设计为可回滚的)。复杂的事务控制可能需要更深层次的Python编程。
  • 性能: 避免在频繁触发的动作中执行耗时操作。对于可能长时间运行的任务,考虑将其委托给Odoo的队列任务 (queue_job 模块) 实现异步执行。

结论

Odoo的服务器动作是实现业务流程自动化和系统功能扩展的强大基石。通过理解不同动作类型的特性、掌握其配置方法,并遵循最佳实践,您可以有效地利用这一工具来满足各种复杂的业务需求,减少人工干预,提高数据准确性,并最终提升企业运营效率。

鼓励您在测试环境中多加实践,从简单的动作开始,逐步探索更高级的组合与Python编程应用,以充分发挥服务器动作的潜力。


四、CRM实战教程:轻松创建“商机赢得后自动跟进与标记”规则

欢迎使用Odoo CRM!在本教程中,我们将学习如何创建一个自动化规则。当您的“线索/商机 (Lead/Opportunity)”状态变为“已赢得 (Won)”时,系统将自动:

  1. 为该商机的销售负责人安排一个“感谢客户”的待办活动,计划在3天后执行。
  2. 将该商机关联的客户(Customer)添加到一个名为“VIP客户”的特定标签下。

通过这个练习,您将掌握Odoo自动化规则的核心配置方法,并能将其应用到更多业务场景中。让我们开始吧!

准备工作 (Prerequisites)

在开始配置之前,请确保以下事项已准备就绪:

  1. CRM应用已安装: 确保您的Odoo实例中已经安装了“CRM”应用。
  2. 开发者模式已激活: 这是访问某些技术菜单(如服务器动作、自动化规则)的前提。
    • 如何激活:通常在 设置 (Settings) 应用中,页面底部或用户菜单(右上角)有“激活开发者模式 (Activate the developer mode)”的链接。
  3. 存在“VIP客户”标签:
    • 您需要一个“客户标签 (Customer Tag)”用于标记VIP客户。请检查或创建它:
      • 导航到 联系人 (Contacts) 应用。
      • 在菜单中选择 配置 (Configuration) > 联系人标签 (Contact Tags)
      • 确认存在一个名为“VIP客户”的标签。如果没有,请点击“创建 (Create)”按钮新建一个,名称就叫“VIP客户”。记下其准确名称。
  4. 了解“待办事项”活动类型: Odoo的活动 (Activity) 有不同类型。确保您有一个代表“待办事项 (To Do)”的活动类型。
    • 检查路径:设置 (Settings) > 技术 (Technical) > 讨论 (Discuss) > 活动类型 (Activity Types)。通常系统会自带“To Do”。
  5. 了解模型名称: 我们将操作“线索/商机”模型,其技术名称是 crm.lead。客户模型是 res.partner,客户标签模型是 res.partner.category

一切就绪?那我们开始配置吧!


第一部分:创建服务器动作 (Server Actions)

服务器动作定义了“系统要做什么”。我们需要创建两个独立的动作,然后将它们合并。

导航至服务器动作:

  • 确保开发者模式已激活。
  • 前往 设置 (Settings) > 技术 (Technical) > 自动化 (Automation) > 服务器动作 (Server Actions)
步骤A: 创建“安排感谢活动”服务器动作

这个动作的目的是在商机负责人名下创建一个待办活动。

  1. 在“服务器动作”列表视图中,点击“创建 (Create)”按钮。
  2. 填写表单如下:
    • 动作名称 (Action Name): 输入 SA - CRM - 安排感谢客户活动
      • 为什么? 清晰的命名有助于后续管理和识别。SA代表Server Action。
    • 模型 (Model): 选择 线索/商机 (crm.lead)
      • 为什么? 这个动作是针对“线索/商机”记录执行的。
    • 动作类型 (Action Type): 选择 创建下一个活动 (Create Next Activity)
      • 为什么? 这是Odoo内置的用于生成计划活动的功能。
    • 在下方的“创建活动 (Create Activity)”配置区域:
      • 活动类型 (Activity Type): 选择 待办事项 (To Do) (或其他您准备好的代表待办的活动类型)。
        • 为什么? 定义了活动的性质。
      • 摘要 (Summary): 输入 感谢客户 - 商机: 然后点击右侧的“动态占位符生成器”图标(一个小标签图标),选择 记录字段 (Record field) > 名称 (name)。最终它看起来像 感谢客户 - 商机: {{object.name}} 或者,您也可以直接使用Python表达式:f"感谢客户 - 商机: {record.name}"
        • 为什么? 这样活动标题会自动包含商机名称,更具可读性。record.nameobject.name 指的是当前商机记录的名称。
      • 负责人 (Assigned to): 选择 记录的负责人 (Salesperson of the Record)。或者,如果使用表达式,选择 Python表达式 (Python expression) 并输入 record.user_id.id
        • 为什么? record.user_id 是指当前商机的销售负责人。.id 是获取其ID。活动将分配给该用户。
      • 截止日期 (Due Date In): 在数字框输入 3,单位选择 天 (days)
        • 为什么? 活动将在当前日期3天后到期。
      • 备注 (Note): (可选) 您可以添加更多说明,例如:“请致电或邮件感谢客户,并确认初步合作满意度。”
  3. 点击“保存 (Save)”。

太棒了!第一个服务器动作创建完毕。

步骤B: 创建“为客户添加VIP标签”服务器动作

这个动作的目的是找到商机关联的客户,并给这个客户打上“VIP客户”的标签。

  1. 返回“服务器动作”列表视图,再次点击“创建 (Create)”按钮。
  2. 填写表单如下:
    • 动作名称 (Action Name): 输入 SA - CRM - 为客户添加VIP标签
    • 模型 (Model): 选择 线索/商机 (crm.lead)
    • 动作类型 (Action Type): 选择 执行Python代码 (Execute Python Code)
      • 为什么? 为关联记录(客户是商机的关联记录 partner_id)的 Many2many 字段(如标签 category_id)添加值,使用Python代码最为直接和可靠。
    • 在“Python代码 (Python Code)”区域,输入以下代码:
# 定义VIP客户标签的准确名称
vip_tag_name = 'VIP客户' # 请确保这个名称与您在Odoo中设置的标签名称完全一致

# 搜索VIP客户标签的ID
vip_tag = env['res.partner.category'].search([('name', '=', vip_tag_name)], limit=1)

if not vip_tag:
    log(f"错误:未找到名为 '{vip_tag_name}' 的客户标签。", level='error')
    # 您也可以选择抛出UserError来中断操作并通知用户
    # from odoo.exceptions import UserError
    # raise UserError(f"未找到名为 '{vip_tag_name}' 的客户标签,请先创建它。")
else:
    # 记录集可能有多条记录,我们遍历它们
    for lead in records: # 'records' 是当前操作的商机记录集
        if lead.partner_id: # 检查商机是否已关联客户
            # 使用 (4, tag_id, 0) 的命令格式为 Many2many 字段添加关联
            # 如果标签已存在,Odoo会自动处理,不会重复添加
            lead.partner_id.write({'category_id': [(4, vip_tag.id)]})
            log(f"已为客户 '{lead.partner_id.name}' (来自商机 '{lead.name}') 添加了 '{vip_tag_name}' 标签。")
        else:
            log(f"商机 '{lead.name}' 未关联任何客户,无法添加VIP标签。", level='warning')
      • 代码解释:
        • vip_tag_name = 'VIP客户': 定义了我们要查找的标签名称。请务必确保这里的名称和您系统中客户标签的实际名称一致!
        • env['res.partner.category'].search(...): 在“客户标签”模型中查找名为 vip_tag_name 的标签。
        • if not vip_tag:: 如果找不到标签,则记录一条错误日志。
        • for lead in records:: 因为服务器动作可能作用于多条商机记录,我们遍历每一条。
        • if lead.partner_id:: 检查当前商机是否有链接的客户 (partner_id 字段)。
        • lead.partner_id.write({'category_id': [(4, vip_tag.id)]}): 这是关键!lead.partner_id 指向客户记录。.write() 用于更新记录。category_id 是客户模型上存储标签的字段(这是一个Many2many字段)。 (4, ID) 是一种特殊命令,用于向Many2many字段添加一个已存在的记录ID,如果关联已存在则不会重复。
        • log(...): Odoo的日志功能,方便调试。
  1. 点击“保存 (Save)”。

第二个动作也完成了!您做得很好!

步骤C: 创建“组合服务器动作”

现在,我们将上面两个独立的动作合并成一个“主”服务器动作。这样做的好处是,自动化规则只需要调用这一个主动作即可,使配置更清晰。

  1. 返回“服务器动作”列表视图,再次点击“创建 (Create)”按钮。
  2. 填写表单如下:
    • 动作名称 (Action Name): 输入 SA - CRM - 商机赢得后组合处理
    • 模型 (Model): 选择 线索/商机 (crm.lead)
    • 动作类型 (Action Type): 选择 执行多个动作 (Execute several actions)
    • 在下方的“动作 (Actions)”标签页中:
      • 点击“添加一行 (Add a line)”。
      • 在弹出的列表中,搜索并选择我们第一步创建的动作:SA - CRM - 安排感谢客户活动
      • 再次点击“添加一行 (Add a line)”。
      • 搜索并选择我们第二步创建的动作:SA - CRM - 为客户添加VIP标签
      • 为什么? 这样设置后,当这个组合动作被调用时,它会按顺序执行这两个子动作。
  3. 点击“保存 (Save)”。

至此,所有需要的服务器动作都准备完毕了!这是非常关键的一步。


第二部分:创建自动化规则 (Automated Action)

自动化规则定义了“何时”以及“在什么条件下”执行我们上面创建的服务器动作。

导航至自动化规则:

  • 确保开发者模式已激活。
  • 前往 设置 (Settings) > 技术 (Technical) > 自动化 (Automation) > 自动化规则 (Automated Actions)
步骤D: 配置自动化规则主体
  1. 在“自动化规则”列表视图中,点击“创建 (Create)”按钮。
  2. 填写表单如下:
    • 名称 (Name): 输入 CRM - 商机赢得后自动跟进与标记
    • 模型 (Model): 选择 线索/商机 (crm.lead)
      • 为什么? 规则将监控“线索/商机”对象的特定变化。
    • 触发 (Trigger): 选择 更新时 (On Update)
      • 为什么? 我们希望在商机记录的某个字段(阶段)被更新时触发此规则。
步骤E: 设置触发条件

这是规则的核心,定义了什么情况下会触发。

  1. 切换到“触发条件 (Trigger Conditions)”标签页(在某些Odoo版本中,这些字段可能直接在主表单上)。
  2. 触发字段 (Triggering Fields / Filter On): (在较新版本Odoo中可能称为 "监测字段" (Monitored Fields) 或 "触发字段" (Trigger Fields))
    • 点击“添加一行 (Add a line)” (如果适用)。
    • 选择字段 阶段ID (stage_id)
      • 为什么? 我们只关心“阶段”字段的变化。指定触发字段可以提高性能,避免不必要的规则检查。
  3. 更新前应用域 (Before Update Domain / Filter Pre-Domain):
    • 输入 [("stage_id.is_won", "=", False)]
      • 为什么? 这个条件确保规则只在商机之前不处于“已赢得”阶段的情况下才继续评估。is_won 是“阶段 (crm.stage)”模型上的一个布尔字段,用于标记该阶段是否代表赢得。这可以防止商机从一个“已赢得”阶段变到另一个“已赢得”阶段(如果存在多个)时重复触发。
  4. 应用域 (Apply on / Filter Domain):
    • 输入 [("stage_id.is_won", "=", True)]
      • 为什么? 这个条件确保规则只在商机更新后的新阶段是“已赢得”阶段时才执行关联的服务器动作。结合上一个“更新前应用域”,我们就精确地捕捉了“商机刚刚变为已赢得”的这一刻。
    • 关于域 (Domain) 的说明:
      • [("field_name", "operator", value)] 是Odoo中标准的域表达式格式。
      • stage_id.is_won 表示我们通过商机上的 stage_id 字段,去访问关联的“阶段”记录上的 is_won 字段。
步骤F: 关联服务器动作
  1. 切换到“动作 (Actions)”标签页。
  2. 在“服务器动作 (Server Action)”字段,选择我们之前创建的组合服务器动作:SA - CRM - 商机赢得后组合处理
    • 为什么? 当上述所有触发条件都满足时,系统就会执行这个选定的服务器动作。
  3. 确保“活动 (Active)”复选框是勾选的。
  4. 点击“保存 (Save)”。

恭喜您!自动化规则已经创建并激活了!


第三部分:测试与验证 (Testing & Validation)

现在是激动人心的时刻——测试我们的成果!

  1. 找到或创建一个测试商机:
    • 导航到 CRM 应用。
    • 您可以创建一个新的商机,或者使用一个现有的、尚未“赢得”的商机。
    • 重要: 确保这个商机已关联了一个“客户 (Customer / partner_id)”并且已指定了一位“销售负责人 (Salesperson / user_id)”,因为我们的服务器动作需要这些信息。如果没有,请先编辑商机并填上。
  2. 将商机阶段改为“已赢得”:
    • 打开该商机。
    • 在看板视图或表单视图中,将其阶段拖动或设置为您系统中代表“已赢得 (Won)”的那个阶段(例如,通常看板列的最后一列是“已赢得”)。
  3. 检查活动是否创建:
    • 方法一 (在商机上查看): 刷新商机页面,查看其下方的沟通栏 (Chatter) 或“计划的活动 (Scheduled Activities)”区域,应该会看到一个标题为“感谢客户 - 商机: [您的商机名称]”的待办活动,负责人是该商机的销售员,截止日期是3天后。
    • 方法二 (在销售负责人的活动中查看): 如果您知道销售负责人是谁(或者就是您自己),可以去Odoo主界面右上角的“活动 (Activities)”菜单(时钟图标),筛选查看该销售负责人的待办事项。
  4. 检查客户标签是否更新:
    • 在商机表单视图中,点击“客户 (Customer)”字段旁边的链接,跳转到该客户的联系人表单。
    • 查看该客户的“标签 (Tags)”字段,应该能看到“VIP客户”这个标签已经被添加上去了。
    • 如果客户之前就有其他标签,新标签会追加进去。

如果活动已正确创建,并且客户标签也已更新,那么您的自动化规则就成功运行了!


常见问题排查 (FAQ/Troubleshooting)

  • 规则不触发怎么办?
    • 检查开发者模式: 确保开发者模式是激活的。
    • 检查自动化规则是否激活: 打开自动化规则,确认“活动 (Active)”复选框已勾选。
    • 检查触发条件:
      • 确认您更改的是 stage_id 字段。
      • 仔细检查“更新前应用域”和“应用域”中的 stage_id.is_won 设置是否正确。确保您系统中标记为“已赢得”的阶段其 is_won 字段确实是 True。您可以在 CRM > 配置 (Configuration) > 阶段 (Stages) 中检查每个阶段的这个设置。
    • 检查服务器日志: 如果规则尝试执行但失败,服务器日志(通常在Odoo服务器运行的终端或日志文件中)可能会有错误信息。服务器动作中我们使用了 log() 函数,也可以帮助追踪。
  • 活动未创建或信息不正确?
    • 检查“SA - CRM - 安排感谢客户活动”服务器动作的配置:活动类型、摘要表达式 (record.name)、负责人表达式 (record.user_id.id)、截止日期是否都正确。
    • 确保测试商机有销售负责人 (user_id)。
  • 客户标签未添加?
    • 检查“SA - CRM - 为客户添加VIP标签”服务器动作的Python代码:
      • vip_tag_name = 'VIP客户':这里的名称是否与您系统中“联系人标签”的实际名称完全一致(包括大小写和空格)?
      • 测试商机是否有链接的客户 (partner_id)?
    • 检查服务器日志中是否有关于“未找到标签”或“未关联客户”的日志信息。
  • Python代码报错怎么办?
    • 仔细阅读服务器日志中的错误信息,它通常会指出代码的哪一行出了问题以及错误类型。
    • 常见的Python错误可能包括:字段名写错 (AttributeError)、对象不存在 (NoneType错误)、缩进错误 (IndentationError)等。
    • 可以先在一个安全的测试记录上,通过“服务器动作”表单底部的“在选定记录上执行 (Execute on Selected Records)”按钮(需先选择一条测试记录)来小范围测试Python代码。
  • VIP客户标签不存在?
    • 如果Python代码中 log(f"错误:未找到名为 '{vip_tag_name}' 的客户标签。") 被触发,请务必去 联系人 (Contacts) > 配置 (Configuration) > 联系人标签 (Contact Tags) 检查并创建该名称的标签。

总结 (Conclusion)

恭喜您成功创建并测试了一个非常实用的Odoo CRM自动化规则!通过这个过程,您不仅学会了如何配置服务器动作(包括创建活动、执行Python代码、组合动作)和自动化规则(设置触发器和条件),更重要的是理解了它们如何协同工作以简化日常任务。

Odoo的自动化潜力巨大。希望您能举一反三,将今天学到的知识应用到更多业务场景中,让Odoo成为您业务增长的强大助力!如果您有任何其他问题,随时可以问我。


五、自动化规则:Python代码高级操作指南

Odoo的服务器动作允许通过“执行Python代码”来实现标准UI配置无法满足的复杂业务逻辑和自动化。这份指南将详细介绍在此环境中可用的变量、核心ORM操作、高级编程技巧,并重点强调安全与性能考量。本指南假设您已对Odoo的基本模型、字段概念及Python编程有一定了解。


一、基础执行环境与可用变量

当您在服务器动作中选择“执行Python代码”时,您的脚本将在一个特定的Odoo执行环境中运行。以下是该环境中一些关键的全局变量和工具:

  1. env (Odoo Environment):
    • 用途: Odoo环境对象,是访问Odoo所有资源(如模型、配置、当前用户、数据库游标等)的入口点。
    • 示例: env.cr (数据库游标), env.uid (当前用户ID), env.context (当前上下文), env['res.partner'] (访问res.partner模型)。
  2. model (ORM Model):
    • 用途: 服务器动作表单中“模型 (Model)”字段所指定的那个模型的ORM模型对象。它是一个空的记录集,但可以用来调用该模型的静态方法或进行不依赖特定记录的操作。
    • 示例: 如果服务器动作的“模型”字段设置为 sale.order,则 model 相当于 env['sale.order']
  3. record (Browse Record):
    • 用途: 当服务器动作针对单个记录执行时(例如,由自动化规则在某条记录上触发),record 代表该条记录的浏览对象 (browse record)。如果动作没有特定的单个记录上下文(例如,由计划任务执行且未传递特定记录ID),则record可能为None或一个空记录集。
    • 注意: 检查 if record and record.exists(): 是一个好习惯。
  4. records (Browse Recordset):
    • 用途: 一个记录集 (browse recordset),包含了当前服务器动作所作用的所有记录。这是最推荐使用的变量来处理记录,因为它能统一处理单个和多个记录的场景。
    • 示例: for rec in records: ...
  5. Python标准库模块:
    • time: Python的 time 模块。
    • datetime: Python的 datetime 模块 (包含 datetime, date, timedelta 类)。
    • dateutil: Python的 dateutil 库,特别是 dateutil.relativedelta 用于复杂的日期计算。
    • timezone (通过 pytz 或 Odoo 提供的工具间接访问): 用于处理时区。
  6. log(message, level='info'):
    • 用途: Odoo提供的日志函数,用于将信息输出到服务器日志文件。
    • 级别 (level): debug, info, warning, error, critical
    • 示例: log(f"Processing record ID: {rec.id}", level='debug')
  7. 异常类:
    • Warning(message)UserError(message) (来自 odoo.exceptions):
      • 用途: 用于在特定条件下中断操作并向用户显示友好的提示信息。
      • UserError 通常更适合业务逻辑验证,它会回滚当前的数据库事务。Warning 的行为可能因上下文而异,有时可能不会回滚。
      • 示例: from odoo.exceptions import UserError; raise UserError("客户信用额度不足!")
基础代码模板

以下是一个基础的Python代码模板,展示了如何与Odoo环境交互并处理记录:

# 可用变量说明:
# env: 当前Odoo环境对象
# model: 当前服务器动作关联的模型对象 (空记录集)
# record: (可选) 当前操作的单条记录 (如果适用)
# records: 当前操作的记录集 (推荐使用)
# time, datetime, dateutil, timezone: Python日期时间库
# log(message, level='info'): 日志函数
# UserError(message), Warning(message): Odoo异常类 (需要从 odoo.exceptions 导入)

from odoo.exceptions import UserError, Warning

log(f"Python Server Action started for model: {model._name}", level='info')

if not records or not records.exists():
    log("No records to process in this execution.", level='info')
    # 根据业务需求,此处可以添加无记录时的处理逻辑或直接返回
else:
    for rec in records:
        try:
            log(f"Processing record: {rec.display_name} (ID: {rec.id})", level='debug')

            # ------------------------------------------
            # 在这里编写您的核心业务逻辑
            # 例如: rec.name = rec.name + " (Processed)"
            # ------------------------------------------

        except Exception as e:
            log(f"Error processing record {rec.id}: {str(e)}", level='error')
            # 可选择是否抛出UserError中断整个过程
            # raise UserError(f"处理记录 {rec.id} 时发生错误: {str(e)}")

log("Python Server Action finished.", level='info')

二、核心ORM操作

利用Python代码,您可以执行所有标准的Odoo ORM操作。

A. 记录集操作:读取和更新当前记录

records 变量是一个记录集,您可以迭代它来处理每条记录。

  • 读取字段值:
for rec in records:
    partner_name = rec.partner_id.name  # 读取Many2one关联记录的名称
    total_amount = rec.amount_total     # 读取当前记录的浮点数字段
    log(f"Record: {rec.name}, Partner: {partner_name}, Amount: {total_amount}")
  • 更新字段值:

对记录字段的赋值会自动标记为待写入数据库。这些更改通常在整个服务器动作成功执行完毕后一并提交。

for rec in records:
    if rec.state == 'draft':
        rec.state = 'confirmed' # 更新选择字段
        rec.description = (rec.description or '') + "\nConfirmed via Server Action." # 更新文本字段
        log(f"Record {rec.name} (ID: {rec.id}) confirmed and description updated.")
B. 关联记录操作
  • Many2one 关联 (record.many2one_field_name):

访问 Many2one 字段会返回一个对应的单条记录对象(如果已设置),或一个空记录集。

for rec in records: # 假设 records 是 'sale.order' 的记录集
    if rec.partner_id: # 检查是否存在关联的客户
        # 读取客户信息
        customer_email = rec.partner_id.email
        log(f"Order {rec.name} customer email: {customer_email}")

        # 更新客户信息 (直接在关联记录上操作)
        if not rec.partner_id.comment:
            rec.partner_id.comment = f"First order: {rec.name}"
  • One2many 关联 (record.one2many_field_name):

访问 One2many 字段会返回一个包含所有关联子记录的记录集。

# 示例:更新销售订单的所有订单行,如果订单总额超过5000,则给未打折的行5%折扣
for order in records: # 假设 records 是 'sale.order' 的记录集
    if order.amount_total > 5000:
        for line in order.order_line: # order_line 是 One2many 字段
            if not line.discount: # 只给尚未打折的行应用折扣
                line.discount = 5.0 # 假设 discount 是百分比字段
        log(f"Applied 5% discount to applicable lines of order {order.name}")
    • 创建新的 One2many: 使用特殊的 (0, 0, {values}) 命令元组通过 write 方法。
# 示例:为一个新的(或特定的)销售订单添加一个默认的服务产品订单行
service_product = env['product.product'].search([('default_code', '=', 'SERV001')], limit=1)
if service_product:
    for order in records: # 假设是新创建的订单
        # 检查是否已存在该服务产品行,避免重复添加
        if not any(line.product_id == service_product for line in order.order_line):
            line_values = {
                'product_id': service_product.id,
                'product_uom_qty': 1,
                'price_unit': service_product.list_price,
                # 根据 sale.order.line 模型的必填字段添加其他值
                # 'order_id': order.id, # order_id 通常会自动处理或不需要显式提供
            }
            order.write({'order_line': [(0, 0, line_values)]})
            log(f"Added service product to order {order.name}")
else:
    log("Service product SERV001 not found.", level='warning')
  • Many2many 关联 (record.many2many_field_name):

访问 Many2many 字段会返回一个包含所有关联记录的记录集。

    • 添加关联 (Link): 使用 (4, record_id) 命令。
    • 移除关联 (Unlink): 使用 (3, record_id) 命令。
    • 完全替换关联 (Replace all): 使用 (6, 0, [record_ids]) 命令。
# 示例:为所有“公司”类型的合作伙伴添加“企业客户”标签
corporate_tag = env['res.partner.category'].search([('name', '=', '企业客户')], limit=1)
if corporate_tag:
    for partner in records: # 假设 records 是 'res.partner' 的记录集
        if partner.is_company and corporate_tag not in partner.category_id:
            partner.write({'category_id': [(4, corporate_tag.id)]})
            log(f"Added '企业客户' tag to partner {partner.name}")
else:
    log("Tag '企业客户' not found.", level='warning')
C. 记录搜索与创建
  • 搜索记录 (env['model.name'].search(domain, limit, order, etc.)):
# 示例:查找与当前订单客户相同的所有未付款发票
for order in records: # 假设 records 是 'sale.order'
    if order.partner_id:
        unpaid_invoices = env['account.move'].search([
            ('move_type', '=', 'out_invoice'),
            ('partner_id', '=', order.partner_id.id),
            ('payment_state', 'in', ('not_paid', 'partial')),
            ('state', '=', 'posted')
        ])
        if unpaid_invoices:
            log(f"Customer {order.partner_id.name} has {len(unpaid_invoices)} unpaid invoices.")
            for inv in unpaid_invoices:
                log(f"  Invoice: {inv.name}, Amount Due: {inv.amount_residual}")
  • 统计记录数量 (env['model.name'].search_count(domain)):

len(env['model.name'].search(domain)) 更高效。

  • 通过ID浏览记录 (env['model.name'].browse(ids)):

当您已有一系列记录ID时,用 browse 获取记录集。

  • 创建新记录 (env['model.name'].create({values_dictionary})):

values_dictionary 是一个包含字段名和对应值的字典。

# 示例:当一个潜在客户(Lead)的阶段变为“合格”时,自动创建一个跟进任务
for lead in records: # 假设 records 是 'crm.lead'
    # 假设有个stage_id字段,并且其name或某个标记指示“合格”
    # 为了演示,我们假设合格阶段的 technical name (XML ID 的一部分) 是 'stage_qualified'
    # 更好的做法是检查阶段模型上的一个布尔字段,如 is_qualified
    qualified_stage = env.ref('crm.stage_lead2', raise_if_not_found=False) # 示例 XML ID
    if qualified_stage and lead.stage_id == qualified_stage:
        # 检查是否已存在类似任务,避免重复创建
        activity_model = env['mail.activity']
        domain = [
            ('res_model', '=', 'crm.lead'),
            ('res_id', '=', lead.id),
            ('summary', '=', f'跟进合格线索: {lead.name}'),
            ('user_id', '=', lead.user_id.id if lead.user_id else env.uid) # 分配给线索负责人或当前用户
        ]
        existing_activity = activity_model.search(domain, limit=1)

        if not existing_activity:
            activity_type_todo = env.ref('mail.mail_activity_data_todo', raise_if_not_found=False)
            if not activity_type_todo:
                log("Activity type 'To Do' not found via XML ID mail.mail_activity_data_todo", level="error")
                continue

            activity_values = {
                'activity_type_id': activity_type_todo.id,
                'summary': f'跟进合格线索: {lead.name}',
                'user_id': lead.user_id.id if lead.user_id else env.uid,
                'res_model_id': env['ir.model']._get_id('crm.lead'), # 获取 crm.lead 模型的ID
                'res_id': lead.id,
                'date_deadline': datetime.date.today() + dateutil.relativedelta.relativedelta(days=3),
                'note': f'<p>潜在客户 {lead.name} (ID: {lead.id}) 已合格,请安排跟进。</p>'
            }
            new_activity = activity_model.create(activity_values)
            log(f"为线索 {lead.name} 创建了跟进活动 (ID: {new_activity.id})")
    • 批量创建: env['model.name'].create([list_of_values_dictionaries]) 可以一次创建多条记录,效率更高。

三、高级编程技巧与实例

A. 动态计算

根据记录的多个字段值进行复杂计算,并将结果写入另一字段。

# 示例:根据潜在客户的来源、预算和是否有电话,计算一个“潜在客户评分”
# 假设 crm.lead 模型有自定义字段:x_lead_source_type (selection), x_budget_amount (float), x_has_phone (boolean), x_lead_score (integer)
for lead in records:
    score = 0
    # 来源评分
    source_scores = {'website': 20, 'referral': 30, 'cold_call': 5, 'advertisement': 15}
    score += source_scores.get(lead.x_lead_source_type, 0)

    # 预算评分
    if lead.x_budget_amount >= 50000:
        score += 40
    elif lead.x_budget_amount >= 10000:
        score += 20

    # 电话评分
    if lead.x_has_phone: # 或者直接检查 lead.phone 字段是否有值
        score += 10

    lead.x_lead_score = score
    log(f"Lead {lead.name} calculated score: {score}")
B. 条件逻辑 (if/elif/else)

根据不同的条件执行不同的自动化逻辑。

# 示例:根据销售订单的总金额,自动更新订单的“客户等级” (自定义字段 x_customer_tier)
for order in records: # 假设是 sale.order
    if order.amount_total >= 10000:
        order.x_customer_tier = 'gold'
        # 可能还有其他操作,如添加特定关注者
        gold_team_user = env['res.users'].search([('login', '=', 'gold_team_manager@example.com')], limit=1)
        if gold_team_user and gold_team_user not in order.message_follower_ids.mapped('partner_id'):
             order.message_subscribe(partner_ids=[gold_team_user.partner_id.id])
    elif order.amount_total >= 5000:
        order.x_customer_tier = 'silver'
    else:
        order.x_customer_tier = 'bronze'
    log(f"Order {order.name} customer tier set to {order.x_customer_tier}")
C. 抛出异常 (UserError, Warning)

在特定条件下阻止操作并向用户显示友好提示。

from odoo.exceptions import UserError

# 示例:如果尝试确认一个没有订单行的销售订单,则阻止并提示用户
for order in records: # 假设是 sale.order
    if order.state == 'draft' and not order.order_line:
        # 此处可以用于“按钮”触发的服务器动作,或者自动化规则在尝试更新状态之前
        raise UserError(f"销售订单 '{order.name}' 没有任何订单行,无法确认!请先添加产品。")
    # 其他逻辑,例如:
    # order.action_confirm() # 注意:直接调用action_confirm可能再次触发自动化规则,需要小心设计避免循环
log(f"Validation passed for records.") # 如果没有抛出异常,则会执行到这里

四、安全与性能考量

这是高级应用中至关重要的部分。

A. 安全风险
  • 任意代码执行: Python代码在服务器端执行,拥有Odoo进程的权限。恶意或有缺陷的代码可能对系统造成严重破坏。
  • 数据访问: 执行的代码可以访问和修改Odoo用户(通常是触发操作的用户,或计划任务的执行用户)权限范围内的任何数据。
  • 最佳实践:
    1. 严格权限控制: 只有绝对可信的系统管理员 (base.group_system) 才应有权创建或修改包含Python代码的服务器动作。
    2. 代码审查: 所有写入服务器动作的Python代码都应经过至少另一位有经验的开发者审查。
    3. 避免硬编码敏感信息: 如API密钥等,应通过系统参数 (ir.config_parameter) 或其他安全方式存储和获取。
    4. 输入验证 (如果适用): 虽然服务器动作通常不直接处理用户输入,但如果逻辑依赖于某些可被用户间接影响的数据,需注意验证。
B. 性能影响与优化建议
  • 触发频率: 如果服务器动作与一个高频触发的自动化规则(如每次写入都触发)相关联,低效的代码将严重影响系统性能。
  • 循环中的数据库操作 (N+1问题):
    • 避免: 在迭代一个记录集时,在循环内部重复调用 search()write() 方法。
    • 优化:
      • 预先收集ID: ids_to_process = records.idspartner_ids = records.mapped('partner_id.id')
      • 批量搜索: env['other.model'].search([('field', 'in', list_of_ids)])
      • mapped(): records.mapped('field_name')records.mapped('many2one_field.related_field') 可以高效地获取一批记录的某个字段或关联字段的值列表。
    • 示例 (低效):
# 低效: N次搜索
for rec in records:
    related_partner_email = env['res.partner'].search([('id', '=', rec.partner_id.id)], limit=1).email
    • 示例 (高效):
# 高效: 使用 mapped
partner_emails = records.mapped('partner_id.email') # 如果partner_id可能为空,需要额外处理
# 或者
partner_ids = records.mapped('partner_id').ids # 获取所有相关的partner_id
partners_data = env['res.partner'].search_read([('id', 'in', partner_ids)], ['email'])
# partners_data 是一个字典列表,需要进一步处理
  • 批量写入/创建:
    • 对记录集直接调用 records.write({values}) 可以一次性更新所有记录(如果更新的值相同)。
    • 如果每条记录更新的值不同,则循环是必要的,但ORM会在事务结束时尝试优化写入。
    • 使用 Model.create([list_of_value_dictionaries]) 进行批量创建。
  • search_count() vs len(search()): 如果只需要计数,search_count(domain) 远比 len(search(domain)) 高效。
  • 善用Odoo Shell (odoo shell -d <database_name>): 在编写复杂逻辑前,可以在Odoo Shell中测试ORM查询和代码片段的性能。
  • 异步处理 (queue_job): 对于非常耗时或可能阻塞主线程的操作(例如,调用外部API、生成大型报告),应考虑将其封装在模型的一个方法中,并使用 with_delay() (需安装 queue_job 模块并配置) 将其异步化。服务器动作本身不能直接 with_delay,但它执行的Python代码可以调用一个支持 queue_job 的模型方法。
# 在服务器动作的Python代码中:
# for rec in records:
#     env['your.model'].with_delay(description=f"Processing {rec.name}").your_async_method(rec.id)

# 在 'your.model' 模型中定义:
# from odoo import api, models
# class YourModel(models.Model):
#     _name = 'your.model'
#
#     @api.model
#     def your_async_method(self, record_id):
#         record = self.browse(record_id)
#         # 执行耗时操作...
  • 避免不必要的计算和数据获取: 只读取和计算当前逻辑真正需要的字段和数据。

五、结论

在Odoo服务器动作中使用Python代码为实现高级自动化和定制化业务逻辑提供了无与伦比的灵活性和能力。然而,这种能力也伴随着对安全性和性能的更高要求。通过遵循本指南中概述的最佳实践,仔细规划您的代码逻辑,并在测试环境中进行充分验证,您将能够安全、高效地发挥Python在Odoo自动化中的全部潜力。

始终记住,简洁、清晰且经过良好测试的代码是构建稳定可靠Odoo系统的关键。


六、自动化规则管理与维护最佳实践指南

Odoo的自动化规则(Automated Actions)与服务器动作(Server Actions)是强大的工具,能够将重复性任务自动化,优化业务流程。然而,随着自动化规则数量的增加和复杂性的提升,有效的管理和维护策略变得至关重要,以防止出现性能瓶颈、意外行为或维护难题。本指南将聚焦于调试技术、监控审计以及一套全面的最佳实践清单。


一、调试技术 (Debugging Techniques)

有效的调试是确保自动化规则按预期工作的关键。

A. 日志记录 (log())

在服务器动作的“执行Python代码 (Execute Python Code)”中,使用 log() 函数是追踪代码执行流程和变量状态的首选方法。

  • 使用方法:
# from odoo.tools import log # 通常不需要显式导入,log 已在可用变量中
log("自动化规则执行开始...", level='info')
# ... 你的代码 ...
user_name = record.user_id.name if record.user_id else '未知用户'
log(f"当前处理记录: {record.display_name}, 负责人: {user_name}", level='debug')
# ... 更多代码 ...
if error_condition:
    log("出现错误条件,将执行特定逻辑。", level='warning')
  • 日志级别: 常用的级别有 debug, info, warning, error, critical。在生产环境中,通常应将Odoo服务器的日志级别设置为 infowarning,但在调试特定问题时,可以临时调整为 debug 或在代码中使用更高级别的日志(如 info)确保信息输出。
  • 查看日志:
    • Odoo配置文件 (odoo.conf): 日志文件的路径通常在 odoo.conf 文件中通过 logfile 参数指定。
    • 标准输出 (stdout): 如果Odoo直接在前台运行(例如,在开发或某些Docker容器中),日志会直接输出到终端。
    • Odoo界面: 对于某些由用户操作直接触发的错误(如 UserError),错误信息会直接显示在界面上。但 log() 的输出主要在服务器日志文件中。
B. Odoo开发者模式 (Developer Mode)

开发者模式提供了许多对调试有用的工具和信息:

  • 技术特性: 允许查看和编辑技术字段名(例如,在设置自动化规则的域过滤器时,需要知道字段的技术名 stage_id.is_won)。
  • 直接访问配置: 轻松访问“服务器动作”和“自动化规则”的菜单进行检查和修改。
  • 详细错误信息: 当自动化规则(尤其是同步执行的)导致错误时,开发者模式有时会在用户界面上显示更详细的技术回溯信息。
  • 视图元数据: 通过“编辑表单视图/列表视图”等,可以查看字段属性、上下文等。
  • 技术菜单: 访问“技术”菜单下的各种底层模型和数据,有助于理解数据结构。
C. 分步测试策略 (Phased Testing Strategy)

在将复杂的自动化规则投入生产之前,采用分步测试至关重要:

  1. 独立测试服务器动作 (Server Action Unit Test):
    • 目的: 验证服务器动作本身的核心逻辑是否正确执行“做什么”。
    • 方法:
      • 对于“执行Python代码”类型:可以临时创建一个按钮(在自定义模块的视图中),使其调用此服务器动作,并在一个隔离的测试记录上执行。
      • 对于其他类型(如“更新记录”、“发送邮件”):手动在一个特征明确的测试记录上配置并运行该动作(例如,通过“动作”菜单添加到记录的上下文菜单中,或临时创建一个按钮)。
      • 使用 log() 语句验证代码路径和变量值。
  2. 集成测试自动化规则 (Automated Action Integration Test):
    • 目的: 验证自动化规则的触发条件(“何时”、“何种条件下”)是否正确,以及它是否能正确调用已测试的服务器动作。
    • 方法:
      • 将已独立测试通过的服务器动作关联到自动化规则。
      • 测试环境中,精确模拟触发条件:
        • 创建时 (On Creation): 创建符合条件的新记录。
        • 更新时 (On Update): 修改记录的特定字段,确保“前置条件过滤 (Before Update Domain)”和“应用域 (Apply on)”被正确评估。
        • 基于时间条件 (Based on Timed Condition): 可能需要手动运行相关的计划任务 (Scheduled Action / Cron Job),或调整记录的日期字段并等待计划任务执行。
      • 检查自动化规则是否按预期触发,并且服务器动作是否被执行。
  3. 端到端业务流程测试 (End-to-End Business Scenario Test):
    • 目的: 在模拟真实业务流程的环境中测试整个自动化链条,包括可能的用户交互和连锁反应。
    • 方法: 在预生产/Staging环境中,模拟完整的业务场景,例如从潜在客户创建到订单确认再到发货,并观察相关的自动化规则是否都能正确、高效地执行。

二、监控与审计 (Monitoring and Auditing)

A. 审计追踪 (Audit Trail - Chatter)

Odoo的审计日志(通常称为Chatter或消息列表)是追踪自动化规则行为的重要工具:

  • 自动记录: 许多标准的ORM操作(如 writecreate,特别是当字段设置了 track_visibility 时)会自动在记录的Chatter中留下痕迹。服务器动作若执行这类操作,其结果也会被记录。
  • 自定义日志: 在服务器动作的Python代码中,可以使用 record.message_post(body="自定义消息内容") 来主动在记录的Chatter中发布消息,这对于记录自动化决策或关键步骤非常有用。
record.message_post(
    body=f"自动化规则 '{action.name}' 已执行:客户状态已更新为VIP。",
    subject="客户状态自动更新", # 可选
    message_type='notification', # 可选,影响显示方式
    subtype_xmlid='mail.mt_note' # 可选,使用预定义的子类型
)
  • 用途: 当出现问题或需要理解某个记录为何处于特定状态时,可以查阅其Chatter,追溯是哪个自动化规则(如果留下了明确信息)或用户操作导致的变更。
B. 性能监控 (Performance Monitoring)

低效的自动化规则可能成为系统性能的瓶颈。

  • 识别潜在问题规则:
    • 高频触发: 作用于频繁创建/更新的模型(如 stock.move, account.move.line)上的规则,特别是“更新时”没有精确“触发字段”的。
    • 复杂逻辑: 包含复杂Python代码的服务器动作,尤其是在循环中执行数据库搜索 (search)、创建 (create) 或写入 (write) 操作而未进行优化的。
    • 大数据量扫描: “基于时间条件”的规则,如果其域过滤器 (filter_domain) 不够优化,可能需要扫描大量记录。
    • 连锁反应: 一个自动化规则触发另一个,形成长链条。
  • 检查方法:
    • Odoo服务器日志分析:
      • 启用SQL日志 (--log-level=debug_sql 或在配置文件中设置 log_level = debug_sql) 可以帮助发现由自动化规则触发的过多或过慢的SQL查询。
      • 查找执行时间过长的日志条目,它们可能与某个自动化任务相关。
    • Odoo性能分析器 (Profiler):
      • 在开发或测试环境中,可以通过 werkzeug 的性能分析器(例如,通过启动参数 --dev=werkzeug_profiler 或安装特定社区模块)来识别最耗时的请求和代码路径。如果某个用户操作因为触发了后台自动化而变慢,分析器可以帮助定位。
    • 业务影响评估: 关注用户报告的特定操作缓慢或系统周期性卡顿问题。检查在这些问题发生时是否有相关的自动化规则被触发。
    • 主动审查: 定期审查(例如每季度)最活跃或最复杂的自动化规则,评估其逻辑和潜在性能影响。
    • 模拟负载测试: 在预生产环境中,使用工具或脚本模拟高并发用户操作或大量数据导入,以触发自动化规则并观察其对系统资源(CPU、内存、数据库负载)的影响。

三、最佳实践清单 (Best Practice Checklist)

这是设计、实施和维护自动化规则与服务器动作时应遵循的核心清单:

#

最佳实践

说明与理由

设计与规划

1

清晰的命名规范

自动化规则 (AA): AA - [模块/流程] - [简要描述] (例: AA - CRM - 商机赢单 - 自动创建感谢活动)

服务器动作 (SA): SA - [类型] - [简要描述] (例: SA - Python - 计算线索得分, SA - Email - 发送月度报告) <br> 理由: 提高可查找性、可理解性和团队协作效率。

2

详尽的描述信息

在自动化规则和服务器动作的“帮助 (Help)”或“描述 (Description)”字段中,详细记录其:目的、触发逻辑、预期结果、依赖关系(如依赖其他SA或特定配置)、负责人/创建者、创建/修改日期。 <br> 理由: 便于未来维护、问题排查和知识传承。

3

精确的触发粒度

自动化规则: 为 更新时 (On Update) 触发器务必设置“触发字段 (Triggering Fields)”。结合使用“更新前应用域 (Before Update Domain / filter_pre_domain)”和“应用域 (Apply on / filter_domain)”来确保规则仅在绝对必要的状态转换或条件下触发。 <br> 理由: 显著提升性能,避免不必要的规则评估和执行。

4

避免无限循环

场景: 规则A因字段X的更新而触发 -> 规则A关联的SA更新了字段X -> 再次触发规则A。

规避方法:

1. 确保“更新前应用域”和“应用域”的条件严格区分状态转换(例:仅当状态从draft变为confirmed时触发)。

2. 在SA的Python代码中,先检查目标字段是否已是期望值,若是则不执行更新。

3. 使用一个辅助的布尔型“哨兵”字段 (如 x_aa_processed_flag),SA执行后设置此字段,AA的触发条件中排除已设置此字段的记录。

理由: 维护系统稳定性,防止资源耗尽。

5

单一职责原则 (SRP)

服务器动作: 每个SA应专注于完成一个明确、独立的任务(如发送一种特定邮件,更新一组特定相关的字段)。

自动化规则: 虽然可以调用一个“执行多个动作”类型的SA来编排流程,但该AA的整体业务目标应该是单一且明确的。

理由: 提高SA的可复用性、可测试性和可理解性。简化调试过程。

6

考虑用户体验 (UX)

避免创建会在用户界面操作(如保存记录)时同步执行且耗时过长的自动化。用户操作应快速响应。

对策:

1. 对于耗时任务 (>几秒),在SA的Python代码中通过queue_job模块(需安装)将其转为异步后台任务执行。

2. 对于非实时要求的批量处理,优先考虑使用计划任务(Scheduled Actions)。

理由: 防止UI卡顿或超时,提升用户满意度。

7

确保幂等性 (Idempotency)

(如果适用) 设计SA,使其在意外情况下被重复执行多次时,结果与执行一次相同,或至少不会产生负面累积效应。 例:添加标签(已存在则不操作),设置状态(已是该状态则不重复设置)。 <br> 理由: 增强系统在异常情况下的健壮性。

实施与编码 (针对Python SA)

8

Python代码质量

代码应整洁、可读、有适当注释。遵循Python编码规范 (如PEP8)。复杂逻辑分解为小函数。 <br> 理由: 提高可维护性和团队协作。

9

错误处理与日志

在Python代码中使用 try...except 块捕获潜在异常。使用 log() 记录关键执行步骤、决策点、变量值和错误信息。使用 UserError 向用户反馈业务校验错误。 <br> 理由: 增强健壮性,简化问题定位。

10

ORM性能优化

避免在循环中执行数据库查询 (search) 或写入 (write) (N+1问题)。优先使用 mapped(), filtered(), 批量操作 (create([...]), records.write({...})),以及 search_count()。 <br> 理由: 关键的性能保障措施。

11

安全编码

不要执行来自不可信来源的动态代码或数据。严格限制对 execute_kw 等底层方法的直接使用。确保对 env.cr.execute() 的使用是绝对安全的(通常应避免直接SQL)。 <br> 理由: 防止安全漏洞。

测试与部署

12

全面的测试

执行单元测试(SA独立)、集成测试(AA+SA)、端到端业务流程测试。覆盖正常场景、边界条件、异常数据和潜在的并发场景。 <br> 理由: 确保功能正确性和稳定性。

13

预生产环境验证

始终在与生产环境配置和数据量相似的预生产/Staging环境中对新的或修改的自动化规则进行彻底验证。 <br> 理由: 降低生产环境风险。

维护与文档

14

定期审查与优化

定期(如每半年或每年)审查现有自动化规则的必要性、效率和准确性。淘汰不再需要的规则,优化性能不佳的规则。 <br> 理由: 保持系统整洁高效,适应业务变化。

15

变更管理

对自动化规则的任何修改都应遵循变更管理流程,包括记录变更原因、内容、测试结果和部署日期。 <br> 理由: 提高可追溯性和管理规范性。


四、版本控制与迁移策略 (Version Control & Migration)

在多环境(开发Dev、测试Test/Staging、生产Prod)中管理和迁移自动化规则和服务器动作,确实是一项挑战,因为它们本质上是Odoo数据库中的配置数据。

  • 策略考量:
    1. 手动配置: 对于少量且简单的规则,可以在各环境中手动重建。但这容易出错且效率低下。
    2. Odoo导出/导入 (XML/CSV):
      • 可以尝试导出相关模型的记录(ir.actions.server, ir.cron)。在导入时,需要仔细处理记录ID的依赖关系(如自动化规则关联的服务器动作ID)、外部ID (ir.model.data) 和环境特定的设置。通常比较繁琐且易错。
    3. 自定义迁移脚本 (Odoo Shell / Python):
      • 编写Python脚本,通过Odoo ORM在源环境中读取规则配置,然后以结构化方式(如JSON、XML)导出,再编写脚本在目标环境中导入并重新创建这些规则。可以更好地控制迁移逻辑和依赖处理。
    4. 在自定义模块中定义 (推荐):
      • 首选方案: 对于所有关键的、复杂的或需要版本控制的服务器动作(尤其是包含Python代码的)和自动化规则,应尽可能在自定义Odoo模块的XML文件中进行定义。
        • 服务器动作 (ir.actions.server) 可以通过 <record> 标签在XML中定义。Python代码可以放在XML的 <code> 标签内,或者更推荐的做法是,将Python逻辑封装在自定义模块的模型方法中,服务器动作仅配置为执行该模型方法(如 model.my_custom_python_method())。
        • 自动化规则 (ir.cron - 注意,Odoo的自动化规则模型是 base.automation,之前版本可能是 ir.cron 的一种用法或类似结构,但现在有专门模型。XML定义 base.automation 记录) 也可以在XML中定义。
      • 优势:
        • 版本控制: 所有定义都在 .py.xml 文件中,可以使用Git等版本控制系统进行管理。
        • 迁移便捷: 通过在目标环境中安装/更新自定义模块即可完成部署。
        • 依赖管理: Odoo模块系统本身处理依赖。
        • 代码组织: Python逻辑在模块中更易于组织、测试和维护。
    5. 环境一致性: 尽力保持开发、测试、生产环境的Odoo版本和核心应用配置一致,以减少迁移过程中的意外。
    6. 严格测试: 任何迁移操作后,必须在目标环境(尤其是生产前的Staging环境)进行彻底的功能和性能测试。

五、结论

Odoo的自动化规则和服务器动作是提升业务效率和一致性的宝贵工具。然而,其强大功能需要辅以严谨的管理和维护策略。通过遵循本指南中概述的调试技术、监控方法、最佳实践清单以及版本控制策略,您可以构建一个稳定、高效且易于维护的自动化环境,从而最大限度地发挥Odoo系统的潜力。

记住,持续的关注、主动的优化和细致的测试是确保自动化系统长期健康运行的基石。


七、订阅服务自动化流程:从订单到客户上线

I. 引言

本方案旨在为销售订阅式服务的公司设计一个全自动化的业务流程。当包含订阅类产品的销售订单被确认后,系统将自动触发一系列跨越销售、发票、订阅、项目和CRM模块的操作,以确保流程的连贯性、减少人工干预、提升客户体验和运营效率。

II. 先决条件与假设

  1. 已安装模块: Odoo实例中已安装并配置好以下核心模块:销售 (sale_management)开票 (account_accountant)订阅 (sale_subscription)项目 (project)CRM (crm)
  2. 订阅产品标识: 产品数据中必须有一种方式来识别“订阅类”产品。推荐在“产品模板 (product.template)”上添加一个自定义布尔型字段,例如:x_is_subscription_product (是否订阅产品)。
  3. 预设项目任务: 为新客户上线项目预定义一组标准的初始任务模板/名称 (例如:“欢迎电话”、“技术对接”、“上线培训”)。
  4. 用户权限与开发者模式: 执行配置的用户需要适当的管理员权限,并已激活Odoo的“开发者模式”以访问技术菜单。
  5. 自定义字段 (重要): 为在流程中传递新创建的项目ID,需要在“销售订单 (sale.order)”模型上创建一个自定义字段:
    • 字段名: x_onboarding_project_id
    • 类型: Many2one
    • 关联模型: 项目 (project.project)
    • 用途: 用于存储此自动化流程为该销售订单创建的客户上线项目。

III. 流程蓝图设计

整个自动化流程在“销售订单”被确认且包含订阅产品时启动,并按以下顺序执行:

  1. 销售订单 (Sale Order) 确认: 用户确认销售订单。
    • 系统检查: 订单中是否包含至少一个“订阅类”产品?订单状态是否变为“销售订单”?
  2. 自动创建并发票 (Invoice): (销售/开票模块)
    • 为该销售订单自动创建客户发票。
    • 自动验证 (确认/过账) 该发票。
  3. 自动创建订阅 (Subscription): (订阅模块)
    • 根据销售订单中的订阅产品和客户信息,自动创建一个新的订阅记录。
  4. 自动创建上线项目 (Project): (项目模块)
    • 为客户创建一个新的项目,用于跟踪后续的上线服务。
    • 项目名称格式建议:[客户名称] - [销售订单号] - 上线服务
    • 将新创建的项目ID记录回销售订单的 x_onboarding_project_id 字段。
  5. 自动生成项目初始任务 (Tasks): (项目模块)
    • 在刚刚创建的上线项目中,根据预设模板自动生成一组初始任务。
  6. 记录客户备注 (CRM Note): (CRM/联系人模块)
    • 在销售订单对应客户的联系人记录下,自动记录一条备注,内容包含订阅开始信息和新创建的上线项目链接。

IV. 服务器动作 (Server Actions) 分解

我们将为上述流程中的每个核心步骤设计独立的服务器动作。这些动作将以“销售订单 (sale.order)”作为主要上下文模型,因为流程由销售订单触发。

SA1: 自动创建并验证销售订单发票
  • 服务器动作名称: SA - SO - 创建并验证发票
  • 目标模型 (Model): 销售订单 (sale.order)
  • 动作类型 (Action Type): 执行Python代码 (Execute Python Code)
  • Python代码逻辑 (伪代码/核心逻辑):
# records 代表当前被操作的销售订单记录集
from odoo.exceptions import UserError

for order in records:
    if order.state != 'sale': # 确保订单已确认
        log(f"订单 {order.name} 状态不是 'sale',跳过发票创建。", level='warning')
        continue

    if order.invoice_status == 'invoiced': # 避免重复开票
        log(f"订单 {order.name} 已开票,跳过。", level='info')
        continue

    try:
        # 创建发票
        # Odoo v15+ 使用 _create_invoices 方法,早期版本可能是 action_invoice_create
        # 需要确认您Odoo版本中 sale.order 模型的开票方法
        invoice_ids = order._create_invoices() # 返回创建的发票ID列表或记录集

        if not invoice_ids:
            log(f"为订单 {order.name} 创建发票失败,未返回发票ID。", level='error')
            # raise UserError(f"为订单 {order.name} 创建发票失败。") # 可选:中断流程
            continue

        # Odoo 15+ 返回的是 account.move 记录集
        created_invoices = env['account.move'].browse(invoice_ids.ids if hasattr(invoice_ids, 'ids') else invoice_ids)

        for inv in created_invoices:
            if inv.state == 'draft':
                inv.action_post() # 验证/过账发票
                log(f"为订单 {order.name} 创建并验证了发票 {inv.name} (ID: {inv.id})")

    except Exception as e:
        log(f"为订单 {order.name} 创建或验证发票时出错: {str(e)}", level='error')
        # 根据业务需求决定是否抛出UserError中断整个自动化流程
        # raise UserError(f"为订单 {order.name} 处理发票时出错: {str(e)}")
SA2: 自动创建客户订阅
  • 服务器动作名称: SA - SO - 创建客户订阅
  • 目标模型 (Model): 销售订单 (sale.order)
  • 动作类型 (Action Type): 执行Python代码 (Execute Python Code)
  • Python代码逻辑 (伪代码/核心逻辑):
    • Odoo的 sale_subscription 模块通常在销售订单确认时,如果订单行产品配置为订阅产品且设置了订阅模板,会自动创建订阅。
    • 如果需要更精细的控制或默认行为不满足,才需要自定义Python。假设标准行为满足,此SA可能仅用于日志记录或确保订阅已创建。
    • 如果标准行为不满足(例如,需要基于特定逻辑选择订阅模板),则需要编写代码。
# records 代表当前销售订单
from odoo.exceptions import UserError

for order in records:
    # 检查是否已有订阅(标准流程可能已创建)
    existing_subscriptions = env['sale.subscription'].search([('sale_order_id', '=', order.id)])
    if existing_subscriptions:
        log(f"订单 {order.name} 已存在订阅: {existing_subscriptions.mapped('name')}", level='info')
        # 可选:如果需要,可以更新这些订阅的状态或某些字段
        # existing_subscriptions.write({'stage_id': env.ref('sale_subscription.sale_subscription_stage_active').id})
        continue

    # 如果标准流程未自动创建,或需要自定义创建逻辑:
    # 筛选出订单中的订阅产品线
    subscription_lines = order.order_line.filtered(
        lambda line: line.product_id.x_is_subscription_product # 使用前提中定义的字段
    )

    if not subscription_lines:
        log(f"订单 {order.name} 中未找到明确的订阅产品线,跳过自定义订阅创建。", level='info')
        continue

    # 假设每个订阅产品线都对应一个订阅,或者合并处理
    # 以下为简化逻辑:为整个订单创建一个订阅,可能需要根据实际业务细化
    # 通常需要一个订阅模板 (subscription.template)
    default_template = env['subscription.template'].search([], limit=1) # 获取一个默认模板,实际应精确查找
    if not default_template:
        log(f"未找到默认订阅模板,无法为订单 {order.name} 创建订阅。", level='error')
        # raise UserError("未配置订阅模板,无法自动创建订阅。")
        continue

    try:
        sub_vals = {
            'name': f"订阅 - {order.name}",
            'partner_id': order.partner_id.id,
            'pricelist_id': order.pricelist_id.id,
            'company_id': order.company_id.id,
            'sale_order_id': order.id, # 关联回销售订单
            'template_id': default_template.id, # 使用查找到的模板
            # 其他必要字段,如 recurring_invoice_line_ids (可能需要从order_line转换)
            # ...
        }
        # 根据订阅产品的具体配置,可能需要更复杂的逻辑来填充订阅行
        # 例如,从 order.order_line 创建 subscription.line
        subscription_lines_vals = []
        for line in subscription_lines:
            subscription_lines_vals.append((0, 0, {
                'product_id': line.product_id.id,
                'name': line.name,
                'quantity': line.product_uom_qty,
                'uom_id': line.product_uom.id,
                'price_unit': line.price_unit,
                # 'analytic_account_id': ...,
            }))
        sub_vals['recurring_invoice_line_ids'] = subscription_lines_vals

        new_subscription = env['sale.subscription'].create(sub_vals)
        # 通常新订阅创建后需要确认,使其进入 'in_progress' 状态
        if new_subscription.stage_id.category == 'draft': # 检查是否在草稿阶段
             new_subscription.action_confirm_subscription() # 或者类似的方法使其生效

        log(f"为订单 {order.name} 创建了新订阅 {new_subscription.name} (ID: {new_subscription.id})")

    except Exception as e:
        log(f"为订单 {order.name} 创建订阅时出错: {str(e)}", level='error')
        # raise UserError(f"为订单 {order.name} 创建订阅时出错: {str(e)}")
    • 注意: 标准Odoo sale_subscription 模块可能在SO确认时自动处理订阅创建。如果如此,此SA可以简化为检查或调整已创建的订阅。如果需要自定义,上述代码提供了一个起点。
SA3: 自动创建客户上线项目 (并记录项目ID回SO)
  • 服务器动作名称: SA - SO - 创建客户上线项目
  • 目标模型 (Model): 销售订单 (sale.order)
  • 动作类型 (Action Type): 执行Python代码 (Execute Python Code)
  • Python代码逻辑 (伪代码/核心逻辑):
# records 代表当前销售订单
for order in records:
    if order.x_onboarding_project_id: # 避免重复创建项目
        log(f"订单 {order.name} 已关联上线项目 {order.x_onboarding_project_id.name},跳过创建。", level='info')
        continue

    project_name = f"{order.partner_id.name if order.partner_id else '未知客户'} - {order.name} - 上线服务"
    project_vals = {
        'name': project_name,
        'partner_id': order.partner_id.id if order.partner_id else False,
        'user_id': order.user_id.id if order.user_id else False, # 项目经理默认为销售负责人
        'sale_order_id': order.id, # 关联销售订单 (如果项目模型有此字段)
        # 'allow_billable': True, # 根据需要设置是否可开单
        # 'company_id': order.company_id.id,
        # 您可能有一个默认的项目模板或类型用于上线项目
        # 'project_template_id': env.ref('your_module.onboarding_project_template_id').id,
    }
    try:
        new_project = env['project.project'].create(project_vals)
        log(f"为订单 {order.name} 创建了上线项目 {new_project.name} (ID: {new_project.id})")

        # 将新创建的项目ID写回销售订单的自定义字段
        order.write({'x_onboarding_project_id': new_project.id})
        log(f"已将项目ID {new_project.id} 更新到订单 {order.name} 的 x_onboarding_project_id 字段。")

    except Exception as e:
        log(f"为订单 {order.name} 创建项目时出错: {str(e)}", level='error')
        # raise UserError(f"为订单 {order.name} 创建项目时出错: {str(e)}")
SA4: 自动生成项目初始任务
  • 服务器动作名称: SA - SO - 生成项目初始任务
  • 目标模型 (Model): 销售订单 (sale.order)
  • 动作类型 (Action Type): 执行Python代码 (Execute Python Code)
  • Python代码逻辑 (伪代码/核心逻辑):
# records 代表当前销售订单
# 预设任务列表,可以更灵活地从配置或项目模板获取
PRESET_TASKS = [
    {'name': '客户欢迎电话', 'deadline_days': 2, 'description': '与客户进行首次沟通,介绍上线流程。'},
    {'name': '技术需求对接', 'deadline_days': 5, 'description': '收集客户技术配置信息和集成需求。'},
    {'name': '产品上线培训', 'deadline_days': 10, 'description': '为客户提供产品使用培训。'},
    # 可以添加更多任务,如 user_id (负责人) 等
]

for order in records:
    if not order.x_onboarding_project_id:
        log(f"订单 {order.name} 未关联上线项目,无法创建任务。", level='warning')
        continue

    project = order.x_onboarding_project_id
    project_manager = project.user_id # 获取项目经理

    # 检查是否已为该项目生成过这批任务,避免重复
    task_count = env['project.task'].search_count([('project_id', '=', project.id)])
    if task_count > 0 and any(task_template['name'] in project.task_ids.mapped('name') for task_template in PRESET_TASKS):
         log(f"项目 {project.name} 似乎已包含初始任务,跳过任务创建。", level='info')
         continue

    task_vals_list = []
    for task_template in PRESET_TASKS:
        deadline = datetime.date.today() + dateutil.relativedelta.relativedelta(days=task_template['deadline_days'])
        task_vals_list.append({
            'name': task_template['name'],
            'project_id': project.id,
            'user_ids': [(6, 0, [project_manager.id])] if project_manager else False, # 分配给项目经理
            'date_deadline': deadline,
            'description': task_template.get('description', ''),
            # 'stage_id': env.ref('project.project_stage_0').id # 初始阶段
        })
    
    if task_vals_list:
        try:
            created_tasks = env['project.task'].create(task_vals_list)
            log(f"为项目 {project.name} (来自订单 {order.name}) 创建了 {len(created_tasks)} 个初始任务。")
        except Exception as e:
            log(f"为项目 {project.name} 创建任务时出错: {str(e)}", level='error')
            # raise UserError(f"为项目 {project.name} 创建任务时出错: {str(e)}")
SA5: 在客户联系人记录下添加备注
  • 服务器动作名称: SA - SO - 记录客户订阅启动备注
  • 目标模型 (Model): 销售订单 (sale.order)
  • 动作类型 (Action Type): 执行Python代码 (Execute Python Code)
  • Python代码逻辑 (伪代码/核心逻辑):
# records 代表当前销售订单
for order in records:
    if not order.partner_id:
        log(f"订单 {order.name} 未关联客户,无法记录备注。", level='warning')
        continue

    customer = order.partner_id
    project_link_msg = ""
    if order.x_onboarding_project_id:
        # 构建项目链接 (需要知道Odoo基础URL,或仅提供项目名称和ID)
        # base_url = env['ir.config_parameter'].sudo().get_param('web.base.url')
        # project_url = f"{base_url}/web#id={order.x_onboarding_project_id.id}&model=project.project&view_type=form"
        # project_link_msg = f"相关的客户上线项目链接:<a href='{project_url}'>{order.x_onboarding_project_id.name}</a>"
        # 简单版本,不含实际超链接,但包含可识别信息:
        project_link_msg = f"相关的客户上线项目是 '{order.x_onboarding_project_id.name}' (ID: {order.x_onboarding_project_id.id})."


    note_body = f"""
    <p>客户的订阅服务已于 {datetime.date.today().strftime('%Y-%m-%d')} 随销售订单 {order.name} 正式启动。</p>
    <p>{project_link_msg}</p>
    <p>请关注客户上线过程和后续服务。</p>
    """
    try:
        # 在客户的 Chatter 中记录备注
        customer.message_post(
            body=note_body,
            subject=f"订阅服务启动 (订单: {order.name})",
            message_type='comment', # 或 'notification'
            subtype_xmlid='mail.mt_note' # 保证其为内部备注
        )
        log(f"已在客户 {customer.name} (来自订单 {order.name}) 的记录下添加了订阅启动备注。")
    except Exception as e:
        log(f"为客户 {customer.name} 添加备注时出错: {str(e)}", level='error')
        # raise UserError(f"为客户 {customer.name} 添加备注时出错: {str(e)}")

V. 主自动化规则的触发与编排

现在,我们需要一个主自动化规则来触发上述服务器动作的执行,并一个“执行多个动作”类型的服务器动作来编排它们。

SA-Main: 编排所有自动化步骤
  1. 服务器动作名称: SA - MAIN - SO确认后全流程处理 (订阅)
  2. 目标模型 (Model): 销售订单 (sale.order)
  3. 动作类型 (Action Type): 执行多个动作 (Execute several actions)
  4. 配置:
    • 在“动作 (Actions)”标签页下,按以下顺序添加已创建的服务器动作:
      1. SA - SO - 创建并验证发票
      2. SA - SO - 创建客户订阅
      3. SA - SO - 创建客户上线项目
      4. SA - SO - 生成项目初始任务
      5. SA - SO - 记录客户订阅启动备注
    • 重要: 确保这些动作的执行顺序符合业务逻辑和数据依赖关系(例如,必须先创建项目,才能获取项目ID用于后续步骤)。
主自动化规则 (Automated Action)
  1. 自动化规则名称: AA - SO确认 (含订阅产品) - 触发全流程
  2. 模型 (Model): 销售订单 (sale.order)
  3. 触发 (Trigger): 更新时 (On Update)
  4. 触发条件 (Trigger Conditions / "数据满足条件"标签页):
    • 监测字段 (Triggering Fields / Monitored Fields): (如果您的Odoo版本有此选项,请精确指定)
      • 状态 (state)
    • 更新前应用域 (Before Update Domain / Filter Pre-Domain):
      • [('state', 'in', ['draft', 'sent'])]
      • 解释: 确保订单在本次更新前是草稿或已发送报价状态,避免从其他状态(如已取消)变为sale时也触发,或重复触发。
    • 应用域 (Apply on / Filter Domain):
      • 此域用于在记录更新后进行判断。
      • 需要组合两个条件:
        1. 订单状态为“销售订单”。
        2. 订单行中至少有一行产品的 x_is_subscription_product 字段为 True
      • 域表达式:
['&', ('state', '=', 'sale'), ('order_line.product_id.x_is_subscription_product', '=', True)]
        • '&' 表示 AND 条件。
        • ('state', '=', 'sale'): 订单状态为“销售订单”。
        • ('order_line.product_id.x_is_subscription_product', '=', True): 这是一个特殊的域写法,表示销售订单的任意一个订单行 (order_line),其关联的产品 (product_id) 的 x_is_subscription_product 字段为 True
  1. 动作 (Actions / "执行"标签页):
    • 服务器动作 (Server Action): 选择上面创建的编排动作 SA - MAIN - SO确认后全流程处理 (订阅)

VI. 数据传递与关联的进一步说明

本方案设计的核心在于按顺序执行多个独立的服务器动作,并通过在“销售订单 (sale.order)”记录上临时存储关键ID(如 x_onboarding_project_id)来实现后续步骤对这些数据的引用。

  • 销售订单 (record / records)作为上下文: 所有服务器动作都以当前触发自动化规则的销售订单记录(集)作为主要上下文。
  • SA3 的关键作用: “SA - SO - 创建客户上线项目”不仅创建项目,还负责将新项目的ID写回 order.x_onboarding_project_id
  • SA4SA5 的数据来源:
    • SA4 (创建任务) 通过 order.x_onboarding_project_id 获取项目对象,从而在其下创建任务。
    • SA5 (记录备注) 也通过 order.x_onboarding_project_id 获取项目信息,以便在备注中引用。
  • Python表达式的灵活性: 在各个服务器动作的Python代码中,可以直接访问 order (当前销售订单记录) 的所有字段及其关联记录的字段,例如 order.partner_id.name (客户名称), order.user_id.email (销售负责人邮箱) 等,用于填充新创建记录的字段或生成动态内容。
  • 错误处理: 在每个Python服务器动作中,都加入了基础的 try...except 结构和 log() 调用。在生产环境中,应根据业务的关键程度决定是否在发生错误时使用 raise UserError(...) 来中断整个自动化链条的执行,并向用户明确提示问题。如果一个步骤失败,后续步骤是否应继续执行也需要仔细考量。

VII. 结论

此设计方案提供了一个复杂、跨模块自动化业务流程的完整框架。通过将复杂的流程分解为一系列独立的、可管理的服务器动作,并由一个主自动化规则根据精确的条件进行触发和编排,可以显著提升企业运营效率和数据一致性。

实施此方案需要具备Odoo技术知识,特别是对服务器动作、Python编程和域表达式的理解。在部署到生产环境之前,必须在测试环境中进行充分的配置和严格的端到端测试,确保所有步骤按预期工作,并处理好潜在的边界情况和错误。

这份设计文档应作为实施的基础,具体细节可能需要根据您Odoo的确切版本和特定业务需求进行调整。


网站公告

今日签到

点亮在社区的每一天
去签到