OA0 = Omni AI 0
OA0 是一个探索 AI 的论坛
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  odoo-manager: 管理 Odoo 业务对象与元数据 (联系人等)

odoo-manager: 管理 Odoo 业务对象与元数据 (联系人等)

 
  devops ·  2026-02-01 17:02:01 · 3 次点击  · 0 条评论  

name: odoo-manager
description: 通过官方外部 XML-RPC API 管理 Odoo(联系人、业务对象及元数据)。支持使用 execute_kw 对任意模型执行通用 CRUD 操作,并提供针对 res.partner 和模型自省的预置流程。具备上下文感知的 URL、数据库和凭据解析能力,支持动态切换实例与数据库。
homepage: https://www.odoo.com/documentation/
metadata: {"openclaw":{"emoji":"🏢","requires":{"env":["ODOO_URL","ODOO_DB","ODOO_USERNAME","ODOO_PASSWORD"]},"primaryEnv":"ODOO_PASSWORD"}}


Odoo 管理器技能

🔐 URL、数据库与凭据解析

URL 解析

Odoo 服务器 URL 优先级(从高到低):

  1. temporary_url — 用于单次操作的临时 URL
  2. user_url — 当前会话的用户定义 URL
  3. ODOO_URL — 环境变量中的默认 URL

此机制允许您:

  • 在多个 Odoo 实例间切换(生产、预发布、特定客户环境)
  • 测试演示数据库
  • 无需修改全局配置即可处理不同客户环境

示例(概念性):

// 默认:使用环境变量中的 ODOO_URL
{{resolved_url}}/xmlrpc/2/common

// 单次操作覆盖:
temporary_url = "https://staging.mycompany.odoo.com"
{{resolved_url}}/xmlrpc/2/common

// 会话级覆盖:
user_url = "https://client-xyz.odoo.com"
{{resolved_url}}/xmlrpc/2/common

数据库解析

数据库名称 (db) 优先级:

  1. temporary_db
  2. user_db
  3. ODOO_DB

可用于:

  • 在同一 Odoo 服务器上操作多个数据库
  • 在测试与生产数据库间切换

用户名与密钥解析

用户名优先级:

  1. temporary_username
  2. user_username
  3. ODOO_USERNAME

密钥(密码或 API 密钥)优先级:

  1. temporary_api_keytemporary_password
  2. user_api_keyuser_password
  3. ODOO_API_KEY(若设置)或 ODOO_PASSWORD

重要提示:

  • Odoo API 密钥替代密码使用,登录方式相同。
  • 请将密码/API 密钥视为真实密码存储;切勿记录或暴露。

环境变量通过标准 OpenClaw 元数据管理:requires.env 声明必需变量(ODOO_URLODOO_DBODOO_USERNAMEODOO_PASSWORD)。ODOO_API_KEY可选环境变量,存在时替代密码使用;它未列在元数据中,需要时直接在环境中设置即可。

解析后的值

运行时技能始终使用:

  • {{resolved_url}} — 最终 URL
  • {{resolved_db}} — 最终数据库名称
  • {{resolved_username}} — 最终登录名
  • {{resolved_secret}} — 实际用于认证的密码 API 密钥

这些值根据上述优先级规则计算得出。


🔄 上下文管理

temporary_*user_*技能逻辑使用的运行时上下文变量,而非 OpenClaw 元数据字段。OpenClaw 没有 optional.context 元数据键;上下文在运行时动态解析,如下所述。

临时上下文(单次使用)

用户示例:

  • "对于此请求,请使用 Odoo 预发布实例"
  • "仅在此操作中使用 odoo_demo 数据库"
  • "仅为此操作使用此用户登录"

行为:

  • 设置 temporary_*(url、db、username、api_key/password)
  • 用于单次逻辑操作
  • 使用后自动清除

适用于:

  • 比较两个环境间的数据
  • 在不同数据库上运行单次检查

会话上下文(当前会话)

用户示例:

  • "在客户 XYZ 的 Odoo 实例上工作"
  • "此会话中使用 clientx_prod 数据库"
  • "后续操作使用我的管理员账户登录"

行为:

  • 设置 user_*(url、db、username、api_key/password)
  • 在整个当前会话中持续有效
  • 仅被 temporary_* 覆盖或通过清除 user_* 重置

重置上下文

用户示例:

  • "恢复到默认 Odoo 配置"
  • "清除我的 Odoo 用户上下文"

操作:

  • 清除 user_urluser_dbuser_usernameuser_passworduser_api_key
  • 技能回退到环境变量(ODOO_URLODOO_DBODOO_USERNAMEODOO_PASSWORD / ODOO_API_KEY

查看当前上下文

用户示例:

  • "你当前连接到哪个 Odoo 实例?"
  • "显示当前 Odoo 配置"

响应应显示(切勿包含完整密钥):

当前 Odoo 上下文:
- URL:https://client-xyz.odoo.com (user_url)
- 数据库:clientxyz_prod (user_db)
- 用户名:api_integration (user_username)
- 密钥:使用 API 密钥 (user_api_key)
- 备用 URL:https://default.odoo.com (ODOO_URL)
- 备用数据库:default_db (ODOO_DB)

⚙️ Odoo XML-RPC 基础

Odoo 通过 XML-RPC(非 REST)暴露其部分服务器框架。
外部 API 文档:https://www.odoo.com/documentation/18.0/fr/developer/reference/external_api.html

两个主要端点:

  • {{resolved_url}}/xmlrpc/2/common — 认证与元调用
  • {{resolved_url}}/xmlrpc/2/object — 通过 execute_kw 调用模型方法

1. 检查服务器版本

common 端点上调用 version() 以验证 URL 和连通性:

common = xmlrpc.client.ServerProxy(f"{resolved_url}/xmlrpc/2/common")
version_info = common.version()

示例结果:

{
  "server_version": "18.0",
  "server_version_info": [18, 0, 0, "final", 0],
  "server_serie": "18.0",
  "protocol_version": 1
}

2. 认证

common 端点上使用 authenticate(db, username, password_or_api_key, {})

uid = common.authenticate(resolved_db, resolved_username, resolved_secret, {})

uid 是整数用户 ID,将用于所有后续调用。

若认证失败,uidFalse / 0 — 技能应:

  • 告知用户凭据或数据库无效
  • 建议检查 ODOO_URLODOO_DB、用户名和密钥

3. 使用 execute_kw 调用模型方法

object 端点构建 XML-RPC 客户端:

models = xmlrpc.client.ServerProxy(f"{resolved_url}/xmlrpc/2/object")

然后使用 execute_kw,其签名如下:

models.execute_kw(
    resolved_db,
    uid,
    resolved_secret,
    "model.name",     # 例如 "res.partner"
    "method_name",    # 例如 "search_read"
    [positional_args],
    {keyword_args}
)

本技能中的所有 ORM 操作均通过 execute_kw 表达。


🔍 域与数据类型(Odoo ORM)

域过滤器

域是条件列表:

domain = [["field_name", "operator", value], ...]

示例:

  • 所有公司:[['is_company', '=', True]]
  • 法国合作伙伴:[['country_id', '=', france_id]]
  • 概率 > 50% 的线索:[['probability', '>', 50]]

常用运算符:

  • "=""!="">"">=""<""<="
  • "like""ilike"(不区分大小写)
  • "in""not in"
  • "child_of"(层次关系)

字段值约定

  • 整数 / 浮点数 / 字符 / 文本:使用原生类型。
  • 日期 / 日期时间:使用 YYYY-MM-DD 或 ISO 8601 格式的字符串。
  • 多对一:写入时通常发送记录 IDint);读取时通常返回 [id, display_name]
  • 一对多 / 多对多:写入时使用 Odoo 命令列表协议(此处未详述;需要时请参阅 Odoo 文档)。

🧩 通用 ORM 操作(execute_kw)

以下各小节展示典型用户查询及对应的 execute_kw 用法。它们适用于任何模型(不仅是 res.partner)。

用户查询:

  • "列出所有公司合作伙伴"
  • "查找已确认的销售订单"

操作(通用):

ids = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "search",
    [domain],
    {"offset": 0, "limit": 80}
)

注意:

  • domain 是一个列表(可为空 [] 以匹配所有记录)。
  • 使用 offsetlimit 进行分页。

统计记录数(search_count)

用户查询:

  • "有多少合作伙伴是公司?"
  • "统计进行中的任务数量"

操作:

count = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "search_count",
    [domain]
)

按 ID 读取记录(read)

用户查询:

  • "显示合作伙伴 7 的详细信息"
  • "给我这些 ID 的 name 和 country_id 字段"

操作:

records = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "read",
    [ids],
    {"fields": ["name", "country_id", "comment"]}
)

若省略 fields,Odoo 将返回所有可读字段(通常很多)。

一步完成搜索与读取(search_read)

search() + read() 的单次调用快捷方式。

用户查询:

  • "列出公司(名称、国家、备注)"
  • "显示前 5 个合作伙伴及其国家"

操作:

records = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "search_read",
    [domain],
    {
        "fields": ["name", "country_id", "comment"],
        "limit": 5,
        "offset": 0,
        # 可选:"order": "name asc"
    }
)

创建记录(create)

用户查询:

  • "创建新合作伙伴 'New Partner'"
  • "在项目 X 中创建新任务"

操作:

new_id = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "create",
    [{
        "name": "New Partner"
        # 其他字段...
    }]
)

返回新创建记录的 ID。

更新记录(write)

用户查询:

  • "更新合作伙伴 7,更改其名称"
  • "降低这些线索的概率"

操作:

success = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "write",
    [ids, {"field": "new value", "other_field": 123}]
)

注意:

  • ids 是记录 ID 列表。
  • ids 中的所有记录接收相同的值。

用户查询:

  • "删除此测试合作伙伴"
  • "清除这些临时任务"

操作:

success = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "model.name", "unlink",
    [ids]
)

适用于快速查找具有显示名称的模型(如合作伙伴、产品)。

用户查询:

  • "查找名称包含 'Agrolait' 的合作伙伴"

操作:

results = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "name_search",
    ["Agrolait"],
    {"limit": 10}
)

结果为 [id, display_name] 列表。


👥 联系人 / 合作伙伴(res.partner)

res.partner 是 Odoo 中用于联系人、公司及众多业务关系的核心模型。

列出公司合作伙伴

用户查询:

  • "列出所有公司"
  • "显示公司及其国家"

操作:

companies = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "search_read",
    [[["is_company", "=", True]]],
    {"fields": ["name", "country_id", "comment"], "limit": 80}
)

获取单个合作伙伴

用户查询:

  • "显示合作伙伴 7"
  • "给我合作伙伴 7 的国家和备注"

操作:

[partner] = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "read",
    [[7]],
    {"fields": ["name", "country_id", "comment"]}
)

创建新合作伙伴

用户查询:

  • "创建合作伙伴 'Agrolait 2' 作为公司"
  • "创建隶属于公司 X 的个人联系人"

最小化请求体:

partner_id = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "create",
    [{
        "name": "New Partner",
        "is_company": True
    }]
)

其他字段示例:

  • streetzipcitycountry_id
  • emailphonemobile
  • company_type"person""company"

更新合作伙伴

用户查询:

  • "更改合作伙伴 7 的地址"
  • "更新国家和电话"

操作:

models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "write",
    [[7], {
        "street": "New street 1",
        "phone": "+33 1 23 45 67 89"
    }]
)

删除合作伙伴

用户查询:

  • "删除测试合作伙伴 999"

操作:

models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "unlink",
    [[999]]
)

🧱 模型自省(ir.model、ir.model.fields、fields_get)

发现模型的字段(fields_get)

用户查询:

  • "res.partner 有哪些字段?"
  • "显示此模型的字段类型和标签"

操作:

fields = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "res.partner", "fields_get",
    [],
    {"attributes": ["string", "help", "type"]}
)

结果为字段名到元数据的映射:

{
  "name": {"type": "char", "string": "Name", "help": ""},
  "country_id": {"type": "many2one", "string": "Country", "help": ""},
  "is_company": {"type": "boolean", "string": "Is a Company", "help": ""}
}

列出所有模型(ir.model)

用户查询:

  • "我的 Odoo 数据库中有哪些可用模型?"

操作:

models_list = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "ir.model", "search_read",
    [[]],
    {"fields": ["model", "name", "state"], "limit": 200}
)

state 指示模型是在代码中定义("base")还是动态创建("manual")。

列出特定模型的字段(ir.model.fields)

用户查询:

  • "通过 ir.model.fields 给我 res.partner 模型的字段列表"

操作(简化):

partner_model_ids = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "ir.model", "search",
    [[["model", "=", "res.partner"]]]
)
fields_meta = models.execute_kw(
    resolved_db, uid, resolved_secret,
    "ir.model.fields", "search_read",
    [[["model_id", "in", partner_model_ids]]],
    {"fields": ["name", "field_description", "ttype", "required", "readonly"], "limit": 500}
)

⚠️ 错误处理与最佳实践

典型错误

  • 认证失败:URL、数据库、用户名或密钥错误 → authenticate 返回 False 或后续调用失败。
  • 访问权限 / ACLs:用户对模型或记录无权限。
  • 验证错误:必填字段缺失、约束违反。
  • 连接问题:网络错误无法访问 xmlrpc/2/commonxmlrpc/2/object

技能应:

  • 清晰指出问题是连接凭据还是业务验证相关。
  • 建议后续步骤(检查环境变量、上下文覆盖、用户权限)。

分页

  • searchsearch_read 上使用 limit / `offset
3 次点击  ∙  0 人收藏  
登录后收藏  
目前尚无回复
0 条回复
About   ·   Help   ·    
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
Developed with Cursor