跨版本兼容性(Odoo 10-19)¶
MCP 服务器会根据检测到的版本、版本类型和部署方式来解析模型名、字段名和能力。权威且可验证的来源是仓库中的 deltas 映射表(server/src/odoo_mcp/compat/deltas.py);本页只是它的摘要。
v1.5.0 新增
- 权限策略会跟随重命名。 针对本映射表中一对被重命名或合并的名称中任一名称的规则,在所有版本上都同时适用于两者(
account.invoice和account.move、stock.quant.package和stock.package、base.automation和base.action.rule等),对其中任一名称的拒绝都会胜出。参见 优先级。 - 沙箱矩阵(Odoo 10.0 至 19.0)也会对照每个真实的版本系列检查权限策略,并在某个系列缺少相应模型或字段时,跳过该项检查并说明原因。
- Odoo 10 上的
odoo_add_automation会填写base.action.rule的触发器字段,该版本将其命名为kind(而不是trigger)。
检测¶
- 版本:
common.version()的主版本与次版本(server_version_info,或从server_version解析得到);主版本约束在 10-19 之间。稳定系列的次版本为 0,Odoo Online 的saas~<主版本>.N版本线为 N,这些版本线位于两个稳定系列之间:18.0 < saas~18.1 < … < saas~18.4 < 19.0。当ODOO_TRANSPORT_PREF为auto或json2时,读取版本既不登录也不使用已弃用的端点,因此不会在 Odoo 日志中留下任何记录:先用 Web 客户端自己的路由(POST /web/webclient/version_info,10.0 起),再用GET /json/version(19.0+),最后才用经典的common.version。jsonrpc和xmlrpc仍然先用common.version。传输层会保存未约束的版本系列(例如 20.0、saas~21.1),用于它自己的选择。 - 版本类型:版本字符串中的
+e后缀;若无,则探测web_enterprise模块(ir.module.module);若仍无,则为unknown。 - 部署方式:主机为
*.odoo.com⇒saas,否则为onprem。odoo_online_profile还会说明该检测的可信程度(deployment_confidence):当版本字符串是saas~版本线时为version,因为只有 Odoo Online 运行这些版本线(此时即使在自有域名上也会报告saas);当只有主机名起决定作用时为host,因为 Odoo.sh 也使用*.odoo.com,而一个位于自有域名之后、运行稳定系列的 Online 数据库看起来会像onprem。
模型重命名¶
| 现代名称 | 历史名称 | 边界 |
|---|---|---|
account.move |
account.invoice |
参见关于 v10 至 v12 的警告 |
account.move.line |
account.invoice.line |
参见关于 v10 至 v12 的警告 |
stock.package |
stock.quant.package |
v19 新增 |
对于 stock.package,两种名称你都可以传入;resolver 会返回在目标实例中有效的那个。
会计:account.move 与 account.invoice 并不是简单的重命名
在 Odoo 13 中,发票模型 account.invoice 被 合并 进了会计凭证模型
account.move(且 account.invoice.line 被合并进 account.move.line)。但在 Odoo 10 至 12 中,二者作为语义不同的两个模型共存:account.move 是 会计凭证,而 account.invoice 是 发票。
因此,在全局将一个名称替换为另一个是危险的,尤其是在写入和删除 时(odoo_create/odoo_write/odoo_unlink):一个本意针对发票的操作,在 v10 至 v12 上可能落到会计凭证上(或反之)。
在 v10 至 v12 实例上的实用规则:显式命名你想要的模型(发票用 account.invoice,会计凭证用 account.move),并在写入前用
odoo_fields_get 加以验证。在 v13 及更高版本中只存在 account.move,因此这种歧义随之消失。
resolver 已经遵循这一规则:仅在 v13+ 上将 account.invoice 映射为
account.move,并且从不将 account.move 反向改写。
字段变更¶
| 模型 | 字段 | 变更 | 边界 |
|---|---|---|---|
account.move.line |
analytic_account_id → analytic_distribution |
重命名 | v16 |
product.template |
uom_po_id |
已移除(使用 uom_id) |
saas~18.1(Online)与 19.0 |
res.partner |
company_type |
已移除(使用 is_company) |
saas~19.1(Online);19.0 中仍存在 |
这两个边界已对照 Odoo 公开源代码中从 16.0 到 saas-19.4 的每个稳定分支与 saas 分支进行核实。
已移除的字段会连同一条 dropped_fields 警告从查询中被丢弃,而不会让整个调用失败。
能力¶
| 能力 | 可用性 |
|---|---|
api_key_auth |
Odoo ≥ 14(更早版本需要密码) |
update_field_translations |
Odoo ≥ 16(更早版本使用带语言上下文的写入) |
API keys 在 /jsonrpc 与 /xmlrpc/2 上同样被接受(两者都调用 Odoo 的
dispatch_rpc,14 至 19),并且是 JSON-2 唯一接受的凭据。当代理拦截或改动 /jsonrpc 时,auto 传输方式仍会回退 到 XML-RPC。
方法¶
| 方法 | 变更 | 替代做法 |
|---|---|---|
name_get |
v17 弃用,v18 移除 | 读取 display_name(10 至 19) |
read_group(经典) |
19.0 弃用;saas~19.1 至 saas~19.4 中不存在;在 20.0 中以 _read_group 的签名回归 |
formatted_read_group(saas~18.4+) |
formatted_read_group |
saas~18.4 新增(web 模块) |
|
check_access_rights |
19.0 弃用,自 saas~19.1 起移除 | has_access(18.0+) |
方法条目同样是数据(METHOD_DELTAS)。odoo_read_group 自 saas~18.4 起调用 formatted_read_group,并保留经典的参数和输出;参见 工具参考。
RPC 端点¶
v1.4.0 新增:JSON-2 传输
Odoo 19 将 /xmlrpc 和 /jsonrpc 标记为弃用(每次调用都会在 Odoo 服务器日志中留下一条警告),并且 Odoo 22 和 Odoo Online saas~21.1 会移除它们。它们的替代方案 JSON-2(/json/2/<模型>/<方法>)自 saas~18.4 和 19.0 起存在,并且只接受 API key。自 v1.4.0 起,插件会自动使用它。
| 端点 | 版本系列 | 凭据 | 说明 |
|---|---|---|---|
/xmlrpc/2 |
10.0 至 21.0 | 密码,或 API key(14+) | 19.0 弃用(移入自动安装的 rpc 模块);每次调用都会在 Odoo 日志中留下一条警告;在 Odoo 22 和 Odoo Online saas~21.1 中移除 |
/jsonrpc |
12.0 至 21.0 | 密码,或 API key(14+) | 与 /xmlrpc/2 相同(两者都经过 dispatch_rpc,14 至 19) |
JSON-2 POST /json/2/<模型>/<方法> |
saas~18.4、19.0+ | 仅 API key(Authorization: bearer) |
只接受具名参数;X-Odoo-Database 请求头;没有弃用警告 |
auto 传输方式¶
当 ODOO_TRANSPORT_PREF=auto(选项 Transport preference,默认值)时,服务器根据未约束的版本系列、凭据类型以及 ODOO_URL 是否带有 user:pass@(用于网关的基本认证,它需要与 JSON-2 的 key 相同的 Authorization 请求头)来决定:
| 情况 | 顺序 | 说明 |
|---|---|---|
| saas~18.4 之前(Odoo 10 至 18,saas~18.1 至 18.3) | JSON-RPC → XML-RPC | 与以前相同 |
saas~18.4 / 19.0+,有 API key 且没有 user:pass@ |
JSON-2 → JSON-RPC → XML-RPC | 第一次登录后固定为 JSON-2;只有当代理拦截 JSON-2 时才使用 JSON-RPC 和 XML-RPC |
同一范围,使用密码或 user:pass@ |
JSON-RPC → XML-RPC | 自 19.0 起,odoo_version 会返回附带解决办法的 transport_notice,服务器也会记录一次 |
| saas~21.1 / Odoo 22+,有 API key | 仅 JSON-2 | 已经没有其他端点 |
saas~21.1 / Odoo 22+,使用密码或 user:pass@ |
ConfigError |
设置一个 API key 并去掉 user:pass@ |
json2 强制使用 JSON-2;jsonrpc 和 xmlrpc 强制使用对应的端点。它们都不会自行切换传输方式。只有当一次调用不会因此被执行两次时,它才会转到另一种传输方式:版本、登录和读取总是如此;写入仅当请求从未到达 Odoo 时才会转移。调用频率限制(HTTP 429)从不切换传输方式:读取会等待 Retry-After(最多 30 秒,3 次尝试),写入则会被报告。
在 JSON-2 上,odoo_execute 在 ids 中接收记录 id,在 kwargs 中按名称接收其他参数。JSON-2 会把 @api.readonly 方法发送到只读游标,当 Odoo 主机设置了 db_replica_host 时,这个游标就是一个副本:紧接在写入之后的读取在那里可能会有延迟。
Odoo Online 工具¶
六个 Odoo Online 工具在任何 Odoo 10-19 上都可用,并按版本降级(详见 工具参考):
| 工具 | 各版本说明 |
|---|---|
odoo_online_profile |
API keys 自 14 起,过期时间自 18 起;在 19.0 上只有管理员能读取模块列表(对其他用户返回 null,并由 unavailable 说明原因) |
odoo_api_catalog |
/doc-bearer 需要 19.0+ 和管理员的 API key;普通用户得到内置的对照表 |
odoo_access_check |
自 18 起按记录检查(has_access);10-17 使用 check_access_rights;导出组自 16 起 |
odoo_record_documents |
发票已保存的 PDF 自 17 起;这些工具不渲染报表(odoo_report 已弃用,且从未能通过 RPC 工作) |
odoo_import_preview |
16-19,通过 JSON-RPC 或 JSON-2 |
odoo_import |
12-19,通过 JSON-RPC 或 JSON-2;在所有版本上都是原子操作 |
版本类型¶
Enterprise 专属的模型(启发式列表:documents.document、sign.request、account.consolidation.period、quality.check、helpdesk.ticket、planning.slot、appraisal.appraisal)在实例被检测为
Community 时会抛出 CompatError。运行时的模块探测是权威来源。
"best-effort" 边界¶
尚未对照 Odoo 源代码或真实实例确认的边界会标注为 best-effort。由于该映射表是由表驱动测试所覆盖的纯数据,修正一个边界只需一行编辑外加一行测试(参见 贡献)。此外,沙箱矩阵工作流每周都会将 res.partner
条目与真实的 Odoo Community 10.0-19.0 进行比对;自 v1.5.0 起也会检查权限策略:odoo_version 的 policy 块对照 --check-policy、需要 Odoo 元数据的 standard 拒绝、每个已安装委托模型的链接字段、builder 下的 odoo_add_field 和 odoo_add_automation(10.0 上为 base.action.rule),以及对本次运行审计日志的 --verify-audit;自 v1.5.1 起,还会检查按位置传递的上下文中值为列表的 default_<字段> 是否被拒绝。自 v1.6.0 起,服务器在 14.0 起的每个版本系列上用沙箱的 API key 登录(在 19.0 上通过 JSON-2),在 10.0 至 13.0 上用密码登录;工作流会检查两者都不会进入审计日志,并检查第二次 sandbox up 会保留这个 key。
实践中¶
针对 现代名称 编写代码,但 v10 至 v12 的会计模型除外:account.invoice/account.move 是合并而非重命名。在 v10 至 v12 上请明确写出你真正想要的模型(发票用 account.invoice,日记账分录用
account.move),并在写入前用 odoo_fields_get 验证。解析器(resolve_model)仅在 v13+ 将 account.invoice → account.move 映射,绝不会把 account.move 反向改写。如果一次查询返回了 dropped_fields,说明该字段在目标版本中不存在,请在上方表格中查找等价的现代名称。自 v1.5.0 起,权限策略中针对 account.move 的规则在 v10 至 v12 上也会限制 account.invoice,反之亦然。在 v10 至 v12 上,请按名称传递上下文(放在 kwargs 中):这些版本会把按位置传递的对象当作上下文,自 v1.5.1 起策略也会把它当作上下文来检查,自 v1.6.0 起还会拒绝它的既不是模型字段、也不是允许的上下文键的键(参见 已知限制 10)。
Skill 参考:/odoo-tools:odoo-crossversion。