PAYATHON 2026

如何在 Odoo 12 中使用 Python XML-RPC 登记付款?

支付老李

结论

问题不在于 active_ids 中包含多张发票,而在于传给 execute_kw 的参数层级有误。context 被放进了位置参数 args,没有通过关键字参数中的 "context" 传递。

execute_kw 的参数结构如下:

execute_kw(
    database,
    uid,
    password,
    model,
    method,
    args,
    kwargs
)

调用付款验证方法时,应这样写:

self.ODOO_OBJECT.execute_kw(
    self.DATA,
    self.UID,
    self.PASS,
    'account.payment',
    'action_validate_invoice_payment',
    [[payment_id]],
    {'context': payment_context}
)

另外,action_validate_invoice_payment 检查的是 account.payment 记录上的 invoice_ids,并不检查 context 中 active_ids 的数量。如果创建 account.payment 时没有正确关联发票,那么即使 active_ids 只有一个,仍然会出现这个错误。

为什么原参数没有生效

当前传入的参数是:

register_payment_row = [
    [payment_id],
    {
        "active_id": invoice_id,
        "active_ids": [invoice_id],
        ...
    }
]

随后又直接把它作为 execute_kw 的 args:

execute_kw(
    ...,
    'action_validate_invoice_payment',
    register_payment_row
)

这样一来,下面两个值都会作为方法的位置参数传入:

[
    [payment_id],
    payment_context
]

但 Odoo 的 context 不是普通位置参数,必须放在 execute_kw 的第七个参数,也就是 kwargs 中:

{
    'context': payment_context
}

更重要的是,context 中的 active_id 和 active_ids 只表示当前操作来自哪张发票。它们不会在验证阶段自动将该发票写入已有付款记录的 invoice_ids。

正确的验证调用

如果 payment_id 对应的付款记录已经正确关联发票,可以这样调用:

def invoiceRegisterPayment(self, payment_id, invoice_id):
    payment_context = {
        "active_id": invoice_id,
        "active_ids": [invoice_id],
        "active_model": "account.invoice",
        "default_invoice_ids": [[4, invoice_id, 0]],
        "journal_type": "sale",
        "lang": "en_US",
        "search_disable_custom_filters": True,
        "type": "out_invoice",
        "tz": False,
    }

    return self.ODOO_OBJECT.execute_kw(
        self.DATA,
        self.UID,
        self.PASS,
        "account.payment",
        "action_validate_invoice_payment",
        [[payment_id]],
        {"context": payment_context},
    )

这里有两个参数层级需要区分:

[[payment_id]]

外层列表是 XML-RPC 的 args,内层列表是 Odoo recordset 的 ID 列表。

context 需要单独放在:

{"context": payment_context}

创建付款时就要关联发票

如果付款记录此前是通过 XML-RPC 创建的,应在创建阶段设置 invoice_ids,或者通过 context 中的 default_invoice_ids 提供默认值。

更明确的做法是直接设置 invoice_ids:

payment_context = {
    "active_model": "account.invoice",
    "active_id": invoice_id,
    "active_ids": [invoice_id],
}

payment_vals = {
    "invoice_ids": [[4, invoice_id, 0]],
    "journal_id": journal_id,
    "payment_method_id": payment_method_id,
    "amount": amount,
    "payment_date": payment_date,
    "payment_type": "inbound",
    "partner_type": "customer",
    "partner_id": partner_id,
    "communication": invoice_number,
}

payment_id = self.ODOO_OBJECT.execute_kw(
    self.DATA,
    self.UID,
    self.PASS,
    "account.payment",
    "create",
    [payment_vals],
    {"context": payment_context},
)

result = self.ODOO_OBJECT.execute_kw(
    self.DATA,
    self.UID,
    self.PASS,
    "account.payment",
    "action_validate_invoice_payment",
    [[payment_id]],
    {"context": payment_context},
)

对于供应商账单,通常需要根据实际业务改为:

"payment_type": "outbound",
"partner_type": "supplier",

具体需要哪些必填字段,还要看 Odoo 12 中账簿、付款方式及本地化模块的配置。

使用 default_invoice_ids 时的写法

也可以通过 context 设置默认值:

payment_context = {
    "active_model": "account.invoice",
    "active_id": invoice_id,
    "active_ids": [invoice_id],
    "default_invoice_ids": [[4, invoice_id, 0]],
}

创建付款时,再传入该 context:

payment_id = self.ODOO_OBJECT.execute_kw(
    self.DATA,
    self.UID,
    self.PASS,
    "account.payment",
    "create",
    [payment_vals],
    {"context": payment_context},
)

default_invoice_ids 是创建记录时使用的默认值。如果付款记录已经创建完成,之后只在调用 action_validate_invoice_payment 时传入这个默认值,通常不会重新修改付款记录的 invoice_ids。

因此,更稳妥的方式是在创建付款时直接写入:

"invoice_ids": [[4, invoice_id, 0]]

检查付款是否真的关联了发票

调用验证方法前,可以先读取付款记录进行确认:

payment_data = self.ODOO_OBJECT.execute_kw(
    self.DATA,
    self.UID,
    self.PASS,
    "account.payment",
    "read",
    [[payment_id]],
    {
        "fields": [
            "invoice_ids",
            "state",
            "amount",
            "payment_type",
            "partner_type",
        ]
    },
)

print(payment_data)

预期结果类似:

[
    {
        "id": 67,
        "invoice_ids": [119],
        "state": "draft",
        ...
    }
]

如果结果是:

"invoice_ids": []

原因就很清楚了:付款记录没有关联任何发票。此时应修正付款创建过程,而不是继续调整 active_ids。

注意事项

default_invoice_ids 是 x2many 字段的命令列表,在 XML-RPC 中需要保留双层结构:

"default_invoice_ids": [[4, invoice_id, 0]]

不要写成:

"default_invoice_ids": [4, invoice_id, None]

后一种写法不是“命令列表”,Odoo 无法将其正确解释为关联一条发票记录。

context 中通常也不需要手动写死:

"uid": 2

XML-RPC 已经通过 self.UID 完成身份认证。如果 context 中的 uid 与实际登录用户不一致,还可能导致权限或默认值行为混乱。

完整的解决办法有两步:先把 context 放进 execute_kw 的 kwargs,再确保付款创建时已经将当前发票关联到 account.payment.invoice_ids,并且只关联这一张发票。

备注:内容仅供参考。