如何在 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,并且只关联这一张发票。
备注:内容仅供参考。
