PAYATHON 2026

Odoo 14 中 account.payment 的 invoice_ids 被什么替代?

支付老李

结论

Odoo 14 没有用另一个同用途的字段直接替代 account.payment.invoice_ids。

付款与发票的关联改为通过会计分录行核销:

  • 发票对应 account.move。
  • 付款过账后也会生成对应的 account.move。
  • 双方的应收或应付分录行通过 account.move.line 的 reconciliation 机制完成核销。
  • 发票的 payment_state 根据剩余金额和核销状态计算。

通过 API 为指定发票登记付款时,建议使用临时模型 account.payment.register。需要注意,active_model、active_id 和 active_ids 必须放在 RPC 的 context 中,不能作为向导字段传入。创建向导后,还需要调用 action_create_payments()。只调用 create 并不会真正创建和过账付款。

报错原因

错误信息如下:

The register payment wizard should only be called on account.move or account.move.line records.

这是 account.payment.register 在检查上下文时抛出的错误。

你发送的 JSON 把以下内容放进了 create 的字段参数:

{
  "active_model": "account.move",
  "active_id": 364660,
  "active_ids": [364660]
}

这些值不是向导的普通业务字段。向导会从上下文中读取它们:

self.env.context.get('active_model')
self.env.context.get('active_ids')

所以必须通过 RPC 调用的 context 传入。

此外,account.payment.register 是 TransientModel。调用 create 只会生成一条向导记录,真正登记付款还需要执行:

wizard.action_create_payments()

正确的 API 调用流程

假设:

  • 发票的 account.move ID 是 364660
  • 发票已经过账
  • 收款日记账 ID 是 230
  • 付款方式 ID 是 7
  • 付款金额是 5.48

第一步:创建付款登记向导

建议使用 execute_kw,这样可以明确传递关键字参数和上下文:

{
  "jsonrpc": "2.0",
  "method": "call",
  "params": {
    "service": "object",
    "method": "execute_kw",
    "args": [
      "{{databaseName}}",
      {{userId}},
      "{{password}}",
      "account.payment.register",
      "create",
      [
        {
          "journal_id": 230,
          "payment_method_id": 7,
          "amount": 5.48,
          "payment_date": "2022-02-04",
          "communication": "Test Create Invoice 32",
          "payment_difference_handling": "open"
        }
      ],
      {
        "context": {
          "active_model": "account.move",
          "active_id": 364660,
          "active_ids": [364660]
        }
      }
    ]
  },
  "id": 1
}

接口通常会返回向导记录 ID,例如:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 125
}

这里的 125 只是示例,实际应使用接口返回的 ID。

source_amount、source_currency_id、partner_id、payment_type 和 partner_type 等字段通常由向导根据发票计算或自动生成,不建议主动提交。除非定制模块有明确要求,否则只传需要修改的字段会更稳妥。

第二步:执行付款创建

把上一步返回的向导 ID 传给 action_create_payments:

{
  "jsonrpc": "2.0",
  "method": "call",
  "params": {
    "service": "object",
    "method": "execute_kw",
    "args": [
      "{{databaseName}}",
      {{userId}},
      "{{password}}",
      "account.payment.register",
      "action_create_payments",
      [
        [125]
      ],
      {
        "context": {
          "active_model": "account.move",
          "active_id": 364660,
          "active_ids": [364660]
        }
      }
    ]
  },
  "id": 2
}

执行后,向导会按照 Odoo 14 的标准流程完成以下操作:

  1. 创建 account.payment。
  2. 生成并过账付款会计分录。
  3. 核销付款与发票对应的应收或应付分录行。
  4. 重新计算发票的 amount_residual 和 payment_state。

多张发票的情况

如果需要为多张发票统一登记付款,可以把多个 account.move ID 放入 active_ids:

"context": {
  "active_model": "account.move",
  "active_ids": [364660, 364661, 364662],
  "active_id": 364660
}

这些发票通常需要满足向导的组合条件,例如合作伙伴、币种和付款方向必须兼容。最终生成一笔还是多笔付款,还会受到 group_payment 等设置的影响。

为什么 move_id 和 invoice_line_ids 无法达到同样效果

account.payment.move_id 指向付款自身生成的会计分录,不是目标发票。把发票 ID 赋给这个字段,无法建立正确的付款关系。

invoice_line_ids 也不是旧版 invoice_ids 的直接替代字段。Odoo 14 判断发票是否已付款,主要看应收或应付科目的 account.move.line 是否完成核销,而不是查看某条付款记录是否保存了发票 ID。

理论上,也可以通过 API 手动完成以下流程:

  1. 创建 account.payment。
  2. 调用 action_post。
  3. 找到付款和发票的应收或应付分录行。
  4. 调用 account.move.line.reconcile()。

不过,这种做法需要自行处理科目、合作伙伴、币种、部分核销、汇兑差额和付款差额。使用 account.payment.register 更接近 Odoo 界面中的“登记付款”流程,也更不容易造成会计数据不完整。

注意事项

发票必须已经过账

向导应针对已过账的客户发票或供应商账单。草稿发票需要先调用:

account.move.action_post()

全额付款不一定立即显示 paid

发票状态由核销情况和付款分录状态共同决定:

  • 尚未付清时通常显示 partial。
  • 应收或应付项目已经核销,但付款仍需银行对账时,可能显示 in_payment。
  • 完成全部核销并满足相关付款状态条件后,才会显示 paid。

具体状态会受到付款日记账的未结收付款科目配置和银行对账流程影响。因此,应同时检查:

amount_residual
payment_state

不能只根据是否创建了 account.payment 来判断付款是否成功。

active_model 也可以是 account.move.line

如果使用:

"active_model": "account.move.line"

那么 active_ids 必须填写发票的应收或应付分录行 ID,不能填写商品明细行 ID。对于大多数外部 API 场景,直接使用 account.move 和发票 ID 更简单。

定制模块可能改变必填字段

上述调用方式适用于 Odoo 14 的标准付款登记流程。如果系统安装了本地化模块、第三方支付模块或自定义会计模块,向导可能会增加必填字段,或改变付款状态的处理逻辑。这种情况下,仍需把 active_* 放在 context 中,再根据实际模型字段补充必要参数。

备注:内容仅供参考。