PAYATHON 2026

如何使用 Odoo web APIs 为 invoice 注册 payment

支付老李

结论

不能通过调用 account.payment.action_register_payment 来完成付款注册。Odoo 的注册付款本质上是一个向导流程:

  1. 先将发票过账;
  2. 以发票为上下文创建 account.payment.register 临时记录;
  3. 调用向导的 action_create_payments;
  4. 由 Odoo 创建并过账付款,同时核销发票的应收或应付账款。

不要直接修改发票的 payment_state。这个字段由付款、会计分录和核销状态计算得出。

为什么原调用没有生效

原调用有两个问题:

models.execute_kw(
    db,
    uid,
    password,
    'account.payment',
    'action_register_payment',
    [{"active_id": invoice_id}]
)

action_register_payment 通常是发票侧用于打开付款窗口的界面动作,不是 account.payment 上负责创建付款并核销发票的方法。

另外,active_id、active_ids 和 active_model 都属于 Odoo context,不能作为普通位置参数传入。即使接口没有立即报错,也不能说明付款记录已经创建,更不能说明发票已经核销。

使用付款向导注册付款

下面的写法适用于提供 account.payment.register 和 action_create_payments 的 Odoo 版本。不同版本或本地模块使用的字段可能略有差异。

from datetime import date
import xmlrpc.client

models = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/object')

invoice_id = 123
journal_id = 10

# 1. 过账发票
models.execute_kw(
    db,
    uid,
    password,
    'account.move',
    'action_post',
    [[invoice_id]]
)

# 2. 注册付款向导所需的上下文
payment_context = {
    'active_model': 'account.move',
    'active_id': invoice_id,
    'active_ids': [invoice_id],
}

# 3. 创建付款向导
wizard_id = models.execute_kw(
    db,
    uid,
    password,
    'account.payment.register',
    'create',
    [{
        'journal_id': journal_id,
        'payment_date': date.today().isoformat(),
        # 不传 amount 时,通常默认使用发票未付余额
        # 'amount': 100.00,
    }],
    {
        'context': payment_context,
    }
)

# 4. 生成、过账并核销付款
result = models.execute_kw(
    db,
    uid,
    password,
    'account.payment.register',
    'action_create_payments',
    [[wizard_id]],
    {
        'context': payment_context,
    }
)

其中最重要的是这两项:

'active_model': 'account.move'
'active_ids': [invoice_id]

向导根据这些上下文判断要支付哪些发票。在部分版本中,只传 active_id 并不够。

如果当前版本使用的仍是 post,而不是 action_post,可以继续使用已经确认有效的调用:

models.execute_kw(
    db,
    uid,
    password,
    'account.move',
    'post',
    [[invoice_id]]
)

Odoo 不同版本的方法名并不完全一致,不要仅凭示例替换已经确认有效的过账方法。

确认付款结果

付款完成后,可以读取发票的剩余金额和付款状态:

invoice = models.execute_kw(
    db,
    uid,
    password,
    'account.move',
    'read',
    [[invoice_id]],
    {
        'fields': [
            'name',
            'state',
            'payment_state',
            'amount_total',
            'amount_residual',
        ],
    }
)[0]

print(invoice)

全额付款并完成核销后,通常会看到:

{
    'state': 'posted',
    'payment_state': 'paid',
    'amount_residual': 0.0,
}

如果只支付了部分金额,amount_residual 不会归零,payment_state 通常会显示为部分付款状态。

较旧版本的处理方式

部分较旧版本没有独立的 account.payment.register 向导,需要直接创建 account.payment,关联发票后再将付款过账。例如:

payment_id = models.execute_kw(
    db,
    uid,
    password,
    'account.payment',
    'create',
    [{
        'payment_type': 'inbound',
        'partner_type': 'customer',
        'partner_id': partner_id,
        'amount': amount,
        'currency_id': currency_id,
        'journal_id': journal_id,
        'payment_date': date.today().isoformat(),
        'payment_method_id': payment_method_id,
        'invoice_ids': [(4, invoice_id, 0)],
    }]
)

models.execute_kw(
    db,
    uid,
    password,
    'account.payment',
    'post',
    [[payment_id]]
)

这段代码仅适用于确实存在 invoice_ids、payment_method_id 和 post 的版本。新版本通常使用 payment_method_line_id,付款和发票也不再通过相同的字段直接关联。因此,应优先采用当前版本界面实际使用的 account.payment.register 流程。

常见注意事项

  • 客户发票通常使用:

    'payment_type': 'inbound'
    'partner_type': 'customer'
  • 供应商账单通常使用:

    'payment_type': 'outbound'
    'partner_type': 'supplier'
  • journal_id 必须指向允许相应收款或付款方式的银行、现金等日记账。

  • 付款币种、合作伙伴和公司必须与发票兼容。

  • 通过 XML-RPC 调用记录方法时,记录 ID 必须放在列表中,例如 [[wizard_id]]。

  • context 应放在 execute_kw 最后一个 kwargs 字典中,不能混入普通参数。

  • 注册付款后,某些 Odoo 版本或会计配置会先把发票标记为 in_payment。只有银行流水与付款完成核销后,状态才会变为 paid。具体行为取决于付款日记账的 outstanding account 和银行对账配置。

  • 如果 action_create_payments、payment_method_line_id 或其他字段不存在,应先确认实际使用的 Odoo 版本和已安装的会计模块,避免混用不同版本的接口。

备注:内容仅供参考。