Odoo Invoice 跳过验证并直接 Register Payment
支付阿杰
结论
自定义按钮需要按顺序完成两项操作:
- 触发发票验证或过账流程,将发票状态从
draft改为open。 returninvoice_pay_customer()生成的窗口动作,打开 Register Payment 弹窗。
只调用 invoice_pay_customer() 不会自动验证发票。如果不返回该方法生成的 action,客户端也无法显示付款窗口。
推荐写法
根据代码中的 account.invoice 和旧式方法签名,下面的方案适用于仍采用发票工作流的旧版 Odoo,例如部分 Odoo 8 环境:
from openerp import models
from openerp import netsvc
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
def button_custom(self, cr, uid, ids, context=None):
context = context or {}
if len(ids) != 1:
raise Warning('Please select exactly one invoice.')
workflow = netsvc.LocalService('workflow')
workflow.trg_validate(
uid,
'account.invoice',
ids[0],
'invoice_open',
cr
)
return self.invoice_pay_customer(
cr,
uid,
ids,
context=context
)
按钮需要使用 type="object":
<button
name="button_custom"
string="Validate and Register Payment"
type="object"
states="draft"
class="oe_highlight"
/>
invoice_open 是验证发票的工作流信号。工作流成功执行后,发票会进入 open 状态。随后,invoice_pay_customer() 返回打开付款登记向导所需的 action。
使用新 API 的旧版 Odoo
如果当前版本支持 recordset API,可以这样写:
from openerp import api, models
from openerp.exceptions import UserError
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
@api.multi
def button_custom(self):
self.ensure_one()
if self.state == 'draft':
self.signal_workflow('invoice_open')
if self.state != 'open':
raise UserError(
'The invoice could not be validated. '
'Register Payment can only be opened for an open invoice.'
)
return self.invoice_pay_customer()
不同 Odoo 小版本的工作流接口可能有所不同。如果模型提供 action_invoice_open(),优先调用这个业务方法:
@api.multi
def button_custom(self):
self.ensure_one()
if self.state == 'draft':
self.action_invoice_open()
if self.state != 'open':
raise UserError('The invoice could not be validated.')
return self.invoice_pay_customer()
不要同时调用 action_invoice_open() 和 signal_workflow('invoice_open'),以免重复触发验证逻辑。根据当前版本实际提供的接口选择一种即可。
原代码没有生效的原因
原方法有两个问题:
self.invoice_pay_customer(cr, uid, ids, context=context)
第一个问题是没有把方法的返回值交给 Web 客户端。invoice_pay_customer() 通常会返回一个 ir.actions.act_window 字典,用来打开 Register Payment 弹窗,所以必须写成:
return self.invoice_pay_customer(cr, uid, ids, context=context)
第二个问题是,invoice_pay_customer() 只负责准备付款登记界面,不会把草稿发票验证为 open。如果在草稿状态下直接调用,可能会因状态、会计分录或上下文不完整而无法正常运行。
注意事项
- 验证发票可能因缺少会计科目、税务配置、账期、发票日期或凭证序列而失败。不要捕获后忽略这些异常,否则可能出现付款界面已经打开,但发票实际上尚未完成过账的情况。
- Register Payment 向导通常会根据单张发票填写默认金额、币种和往来单位,因此按钮方法最好使用
ensure_one(),或明确要求len(ids) == 1。 account.invoice是旧版模型。较新的 Odoo 使用account.move,通常通过action_post()完成过账,再通过相应的 payment register action 打开付款向导,不能直接套用上面的代码。- 如果继承视图后没有显示按钮,请检查 XML 是否已经加载、按钮是否位于正确的
<header>中,以及states="draft"是否与发票当前状态一致。
备注:内容仅供参考。
