PAYATHON 2026

如何在 odoo10 中通过 psql 完成 invoice payment

支付老李

结论

不要用 psql 直接把 Odoo 10 发票的 state 改为 paid。在 Odoo 10 中,发票的“已付款”状态来自付款凭证过账,以及付款分录与发票应收或应付分录的核销。只修改 account_invoice.state,无法正确完成这些会计处理。

应通过 Odoo ORM 创建并过账 account.payment,再调用付款登记向导完成核销。相关代码可以在服务器的 Odoo shell 中执行。底层数据仍会写入 PostgreSQL,同时能够保留完整的会计逻辑。

推荐操作步骤

操作前先备份数据库,并确认以下事项:

  • 发票已经验证或过账,状态通常为 open。
  • 收款使用的银行或现金日记账配置正确。
  • 客户发票使用入账付款方式,供应商账单使用出账付款方式。
  • 执行账户具有操作会计付款和发票的权限。

进入 Odoo shell:

./odoo-bin shell -d your_database

部分 Odoo 10 部署使用:

./odoo.py shell -d your_database

客户发票收款示例

以下代码根据发票编号查找一张未付款的客户发票,再通过 Odoo 10 的付款登记向导创建付款并完成核销:

invoice = env['account.invoice'].search([
    ('number', '=', 'INV/2017/0001'),
    ('type', '=', 'out_invoice'),
    ('state', '=', 'open'),
], limit=1)

if not invoice:
    raise Exception('未找到处于 open 状态的客户发票')

journal = env['account.journal'].search([
    ('type', 'in', ['bank', 'cash']),
    ('company_id', '=', invoice.company_id.id),
], limit=1)

if not journal:
    raise Exception('未找到可用的银行或现金日记账')

payment_method = journal.inbound_payment_method_ids[:1]

if not payment_method:
    raise Exception('该日记账没有配置入账付款方式')

payment_register = env['account.register.payments'].with_context(
    active_model='account.invoice',
    active_ids=invoice.ids,
).create({
    'payment_date': fields.Date.context_today(invoice),
    'journal_id': journal.id,
    'payment_method_id': payment_method.id,
    'amount': invoice.residual,
})

payment_register.create_payments()

invoice.invalidate_cache()
print(invoice.state)

正常情况下,足额付款并成功核销后,invoice.state 应变为 paid。如果付款金额小于 invoice.residual,发票通常仍处于 open 状态,但剩余未付金额会相应减少。

Odoo 10 的不同补丁版本或定制模块可能会为向导增加必填字段。如果 create() 报告缺少字段,应先通过 Odoo 界面检查付款配置,或者查看当前实例中 account.register.payments 的字段定义。不能假定所有 Odoo 10 数据库的字段和配置都完全一致。

供应商账单付款

为供应商账单付款时,需要使用出账付款方式:

invoice = env['account.invoice'].search([
    ('number', '=', 'BILL/2017/0001'),
    ('type', '=', 'in_invoice'),
    ('state', '=', 'open'),
], limit=1)

if not invoice:
    raise Exception('未找到处于 open 状态的供应商账单')

journal = env['account.journal'].search([
    ('type', 'in', ['bank', 'cash']),
    ('company_id', '=', invoice.company_id.id),
], limit=1)

payment_method = journal.outbound_payment_method_ids[:1]

if not payment_method:
    raise Exception('该日记账没有配置出账付款方式')

wizard = env['account.register.payments'].with_context(
    active_model='account.invoice',
    active_ids=invoice.ids,
).create({
    'payment_date': fields.Date.context_today(invoice),
    'journal_id': journal.id,
    'payment_method_id': payment_method.id,
    'amount': invoice.residual,
})

wizard.create_payments()

为什么不能直接执行 SQL

不要执行下面这种更新:

UPDATE account_invoice
SET state = 'paid'
WHERE number = 'INV/2017/0001';

这条 SQL 只改变了发票表中的状态字段,不会:

  • 创建付款记录;
  • 创建并过账会计凭证;
  • 更新银行或现金科目;
  • 核销应收账款或应付账款;
  • 创建 account_partial_reconcile 或相关核销记录;
  • 正确更新发票剩余金额;
  • 执行 Odoo 的模型约束、权限检查和自动化逻辑。

这样处理后,发票可能显示为已付款,但总账中仍有未核销余额。数据不一致的问题可能会在应收账龄、合作伙伴余额、付款报表或取消发票时暴露出来。

已经用 SQL 改过状态怎么办

不要继续手工补写付款表和核销表。应先将发票恢复到原来的实际状态,再通过 ORM 正常登记付款。恢复之前,需要检查对应的应收或应付分录是否已经核销,因为只看 account_invoice.state 无法判断完整的会计状态。

至少应核对以下数据:

SELECT id, number, state, residual, move_id
FROM account_invoice
WHERE number = 'INV/2017/0001';

SQL 查询可以用于诊断,但修复数据和登记付款应在备份数据库后通过 Odoo ORM 完成。生产环境还应先在数据库副本中验证,尤其是安装了本地化会计模块或自定义付款模块的实例。

备注:内容仅供参考。