如何通过 Odoo 19 JSON2 API 登记发票全额付款?
结论
不要先创建 account.payment,然后把它的 ID 传给 account.payment.register/action_create_payments。
account.payment.register 是临时向导模型,正确的调用顺序是:
- 以发票 ID 作为
active_ids,创建account.payment.register向导。 - 从第一次调用的返回结果中取得向导 ID。
- 使用该向导 ID 调用
action_create_payments。
发票和付款并不是通过 account.payment.invoice_ids 关联的。付款登记向导会根据发票中尚未核销的应收或应付明细创建付款,并完成会计明细核销。
为什么当前调用会失败
第一次调用创建的是:
account.payment
第二次调用执行的却是:
account.payment.register/action_create_payments
这是两个不同的模型,其记录 ID 也没有关联。35 是一条 account.payment 记录的 ID,不是 account.payment.register 向导的 ID。
Odoo 会尝试从 account.payment.register 中读取 ID 为 35 的临时记录。如果该记录不存在、已经过期,或者没有待处理的 line_ids,就会触发以下错误:
You can't open the register payment wizard without at least one receivable/payable line
标准付款登记流程也不应通过下面的参数关联发票:
"invoice_ids": [16]
在 Odoo 的标准会计流程中,付款和发票最终通过应收、应付会计明细之间的核销关系建立关联。
正确的调用流程
第一步:创建付款登记向导
调用:
POST https://myhost/json/2/account.payment.register/create
请求体可以写成:
{
"vals_list": {
"journal_id": 6,
"amount": 7.20
},
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
如果接口要求 vals_list 必须是数组,则使用:
{
"vals_list": [
{
"journal_id": 6,
"amount": 7.20
}
],
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
使用对象还是数组,应以当前 Odoo 19 实例对 create 方法的 JSON2 参数校验结果为准。无论使用哪种形式,都必须正确传入:
"active_model": "account.move",
"active_ids": [16]
创建向导时,Odoo 会根据这些上下文参数读取发票中尚未核销的应收或应付明细,并填充向导的 line_ids。
第一次调用返回的是付款登记向导 ID,例如:
35
或:
[35]
具体返回形式取决于调用参数和实例的序列化方式。
第二步:执行付款登记
使用上一步返回的向导 ID 调用:
POST https://myhost/json/2/account.payment.register/action_create_payments
请求体:
{
"ids": [35],
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
这里的 35 必须来自:
account.payment.register/create
不能来自:
account.payment/create
action_create_payments 会执行以下操作:
- 创建实际的
account.payment; - 过账付款;
- 生成付款对应的会计分录;
- 核销付款和发票的应收或应付明细;
- 更新发票的付款状态及剩余未付金额。
全额付款时的金额处理
如果要支付发票的全部未付金额,可以不传 amount,由向导根据发票的 amount_residual 计算默认值:
{
"vals_list": {
"journal_id": 6
},
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
与在客户端写死 7.20 相比,这种方式通常更稳妥,尤其适用于以下情况:
- 发票已经部分付款;
- 存在多币种;
- 付款日期会影响汇率;
- 发票发生过退款或部分核销;
- 金额存在币种舍入差异。
如果需要指定付款日期,可以在向导中传入:
{
"vals_list": {
"journal_id": 6,
"payment_date": "2026-08-31"
},
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
日期应填写实际付款日期。
显式传递应收或应付明细
通常只需传入 active_model 和 active_ids。如果当前实例中的自定义模块导致向导无法根据上下文完成初始化,可以先查询发票中尚未核销的应收或应付明细,再显式填写 line_ids。
例如调用:
POST https://myhost/json/2/account.move.line/search
请求体:
{
"domain": [
["move_id", "=", 16],
[
"account_id.account_type",
"in",
["asset_receivable", "liability_payable"]
],
["reconciled", "=", false]
]
}
假设返回的明细 ID 为:
[101]
创建向导时,可以使用 x2many 命令:
{
"vals_list": {
"journal_id": 6,
"amount": 7.20,
"line_ids": [
[6, 0, [101]]
]
},
"context": {
"active_model": "account.move",
"active_ids": [16]
}
}
不过,最好还是让向导通过 active_ids 自动获取明细,因为它还会检查币种、合作伙伴、付款方向和批次等信息。
调用前需要确认
发票必须满足以下付款登记条件:
state为posted;amount_residual大于零;- 存在尚未核销的应收或应付明细;
- 收款日记账
journal_id = 6可用于当前公司和付款类型; - 发票与日记账所属公司兼容;
- API 用户具有创建和过账付款,以及核销会计明细的权限。
客户发票通常包含 asset_receivable 明细,供应商账单通常包含 liability_payable 明细。因此,不能只根据是否存在 payable lines 来判断客户发票是否满足条件。
不建议直接创建 account.payment
直接调用:
account.payment/create
只会创建付款记录,不会自动完成付款与发票的核销。即使随后调用 account.payment/action_post,付款通常也只是完成过账,并未与目标发票匹配。
如果必须直接创建付款,还需要:
- 创建并过账
account.payment; - 找到发票中尚未核销的应收或应付明细;
- 找到付款分录中同一账户对应的明细;
- 对这些
account.move.line调用reconcile。
对于普通的发票全额付款,使用 account.payment.register/create 和 account.payment.register/action_create_payments 更符合 Odoo 的标准业务流程。
备注:内容仅供参考。
