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 优先级(从高到低):
temporary_url — 用于单次操作的临时 URLuser_url — 当前会话的用户定义 URLODOO_URL — 环境变量中的默认 URL此机制允许您:
示例(概念性):
// 默认:使用环境变量中的 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) 优先级:
temporary_dbuser_dbODOO_DB可用于:
用户名优先级:
temporary_usernameuser_usernameODOO_USERNAME密钥(密码或 API 密钥)优先级:
temporary_api_key 或 temporary_passworduser_api_key 或 user_passwordODOO_API_KEY(若设置)或 ODOO_PASSWORD重要提示:
环境变量通过标准 OpenClaw 元数据管理:requires.env 声明必需变量(ODOO_URL、ODOO_DB、ODOO_USERNAME、ODOO_PASSWORD)。ODOO_API_KEY 是可选环境变量,存在时替代密码使用;它未列在元数据中,需要时直接在环境中设置即可。
运行时技能始终使用:
{{resolved_url}} — 最终 URL{{resolved_db}} — 最终数据库名称{{resolved_username}} — 最终登录名{{resolved_secret}} — 实际用于认证的密码或 API 密钥这些值根据上述优先级规则计算得出。
temporary_*和user_*是技能逻辑使用的运行时上下文变量,而非 OpenClaw 元数据字段。OpenClaw 没有optional.context元数据键;上下文在运行时动态解析,如下所述。
用户示例:
odoo_demo 数据库"行为:
temporary_*(url、db、username、api_key/password)适用于:
用户示例:
clientx_prod 数据库"行为:
user_*(url、db、username、api_key/password)temporary_* 覆盖或通过清除 user_* 重置用户示例:
操作:
user_url、user_db、user_username、user_password、user_api_keyODOO_URL、ODOO_DB、ODOO_USERNAME、ODOO_PASSWORD / ODOO_API_KEY)用户示例:
响应应显示(切勿包含完整密钥):
当前 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(非 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 调用模型方法在 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
}
在 common 端点上使用 authenticate(db, username, password_or_api_key, {}):
uid = common.authenticate(resolved_db, resolved_username, resolved_secret, {})
uid 是整数用户 ID,将用于所有后续调用。
若认证失败,uid 为 False / 0 — 技能应:
ODOO_URL、ODOO_DB、用户名和密钥为 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 表达。
域是条件列表:
domain = [["field_name", "operator", value], ...]
示例:
[['is_company', '=', True]][['country_id', '=', france_id]][['probability', '>', 50]]常用运算符:
"="、"!="、">"、">="、"<"、"<=""like"、"ilike"(不区分大小写)"in"、"not in""child_of"(层次关系)YYYY-MM-DD 或 ISO 8601 格式的字符串。int);读取时通常返回 [id, display_name]。以下各小节展示典型用户查询及对应的 execute_kw 用法。它们适用于任何模型(不仅是 res.partner)。
用户查询:
操作(通用):
ids = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "search",
[domain],
{"offset": 0, "limit": 80}
)
注意:
domain 是一个列表(可为空 [] 以匹配所有记录)。offset 和 limit 进行分页。用户查询:
操作:
count = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "search_count",
[domain]
)
用户查询:
操作:
records = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "read",
[ids],
{"fields": ["name", "country_id", "comment"]}
)
若省略 fields,Odoo 将返回所有可读字段(通常很多)。
search() + read() 的单次调用快捷方式。
用户查询:
操作:
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"
}
)
用户查询:
操作:
new_id = models.execute_kw(
resolved_db, uid, resolved_secret,
"model.name", "create",
[{
"name": "New Partner"
# 其他字段...
}]
)
返回新创建记录的 ID。
用户查询:
操作:
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]
)
适用于快速查找具有显示名称的模型(如合作伙伴、产品)。
用户查询:
操作:
results = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "name_search",
["Agrolait"],
{"limit": 10}
)
结果为 [id, display_name] 列表。
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}
)
用户查询:
操作:
[partner] = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "read",
[[7]],
{"fields": ["name", "country_id", "comment"]}
)
用户查询:
最小化请求体:
partner_id = models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "create",
[{
"name": "New Partner",
"is_company": True
}]
)
其他字段示例:
street、zip、city、country_idemail、phone、mobilecompany_type("person" 或 "company")用户查询:
操作:
models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "write",
[[7], {
"street": "New street 1",
"phone": "+33 1 23 45 67 89"
}]
)
用户查询:
操作:
models.execute_kw(
resolved_db, uid, resolved_secret,
"res.partner", "unlink",
[[999]]
)
用户查询:
操作:
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": ""}
}
用户查询:
操作:
models_list = models.execute_kw(
resolved_db, uid, resolved_secret,
"ir.model", "search_read",
[[]],
{"fields": ["model", "name", "state"], "limit": 200}
)
state 指示模型是在代码中定义("base")还是动态创建("manual")。
用户查询:
操作(简化):
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}
)
authenticate 返回 False 或后续调用失败。xmlrpc/2/common 或 xmlrpc/2/object。技能应:
search 和 search_read 上使用 limit / `offset