在 odoo9 中付款后如何将 Open 发票转为 Paid
结论
导入发票时,不能只导入发票表头、明细并将状态设为 Open。Odoo 9 判断发票是否为 Paid,看的是发票对应的应收账款分录与付款分录是否已经核销,而不是有没有付款记录。
需要重点检查并核销两条会计分录:
- 发票生成的应收账款借方分录
- 付款生成的应收账款贷方分录
这两条分录必须使用同一个可核销账户,通常还要有相同的客户和币种。完成核销且发票剩余金额变为 0 后,状态才会从 Open 更新为 Paid。
为什么付款成功后仍然是 Open
从 Odoo 7 迁移到 Odoo 9 时,如果直接导入 account.invoice 数据,常会遇到这些问题:
- 发票的
state被导入为open,但没有有效的move_id - 发票有会计凭证,但应收账款分录不完整
- 发票和付款使用了不同的应收账款账户
- 发票分录与付款分录的
partner_id不一致 - 付款已经过账,但相关分录没有执行 reconciliation
- 历史付款信息已经导入,原有的核销关系却没有迁移
- 外币发票仍有汇兑差额,剩余金额不为
0
Allow Reconciliation 只表示该账户可以执行核销,不会自动修复已经错误导入或缺少关联的历史分录。income account 也不决定发票的付款状态。发票是否已付,主要取决于应收账款分录。
建议的处理步骤
1. 检查发票是否有正式会计凭证
打开有问题的发票,检查其 Journal Entry。发票至少应包含:
- 收入或税务相关分录
- 一条客户应收账款分录
- 正确的客户、账户、币种和到期金额
如果发票没有 move_id,说明它只是被设成了 Open,并没有经过 Odoo 9 的正常验证流程。仅使用 Register Payment 无法修复这类数据。
可以先在测试数据库中把发票恢复为 Draft,再重新验证,由 Odoo 9 生成正确的会计凭证。不过,操作前必须确认原导入是否已经生成分录,以免重复记账。
2. 比较发票与付款的应收账款分录
检查发票和付款的 Journal Items,逐项确认:
account_id相同- 账户已启用 reconciliation
partner_id相同- 公司一致
- 币种及外币金额能够匹配
- 两条分录方向相反
- 两条分录都已过账
- 两条分录尚未被其他业务核销
例如,发票使用客户 A,而付款分录没有客户,Odoo 通常无法按预期自动核销。
3. 手工核销已有分录
如果发票和付款都已正确过账,只是缺少核销关系,可以使用会计模块的 Manual Reconciliation 功能:
- 进入对应的应收账款账户。
- 筛选该客户尚未核销的 Journal Items。
- 选择发票分录及对应的付款分录。
- 执行 Reconcile。
- 返回发票,确认剩余金额是否为
0。
核销后,Odoo 会重新计算发票的 residual,并在满足条件时将状态更新为 Paid。
代码核销示例
批量修复时,建议先明确取得每张发票的应收分录 ID 及对应的付款分录 ID,再调用 reconcile()。不要搜索某个客户的全部未核销分录后直接批量核销,否则可能误匹配其他发票或预收款。
invoice_line = env['account.move.line'].browse(invoice_receivable_line_id)
payment_line = env['account.move.line'].browse(payment_counterpart_line_id)
if invoice_line.account_id != payment_line.account_id:
raise Warning('Invoice and payment use different receivable accounts.')
if invoice_line.partner_id != payment_line.partner_id:
raise Warning('Invoice and payment use different partners.')
if not invoice_line.account_id.reconcile:
raise Warning('The receivable account does not allow reconciliation.')
if invoice_line.reconciled or payment_line.reconciled:
raise Warning('One of the journal items has already been reconciled.')
(invoice_line | payment_line).reconcile()
不同的 Odoo 9 安装或定制模块可能使用不同的字段名称。执行代码前,应先在 Odoo shell 或测试数据库中确认实际的模型字段。
也可以检查单张发票的相关数据:
invoice = env['account.invoice'].browse(invoice_id)
print(invoice.state)
print(invoice.residual)
print(invoice.move_id)
for line in invoice.move_id.line_id:
print(
line.id,
line.account_id.code,
line.partner_id.name,
line.debit,
line.credit,
line.reconciled
)
如果当前数据库中的 account.move 使用 line_ids 而不是 line_id,请按实际字段修改:
for line in invoice.move_id.line_ids:
print(line.id, line.account_id.code, line.debit, line.credit)
导入方式需要调整时
如果大量导入的发票都没有正确的会计凭证,可以按以下方式重新迁移:
- 将发票导入为 Draft。
- 正确映射客户、产品、税、收入账户和应收账户。
- 在 Odoo 9 中验证发票,由系统生成会计分录。
- 导入或创建付款。
- 将付款过账。
- 根据发票与付款的对应关系执行核销。
如果迁移的是已经结清的历史发票,也可以根据迁移方案将其作为期初数据处理,但必须保证期初应收余额、付款余额和总账一致。不能只修改发票的 state 字段。
注意事项
不要通过 SQL 或代码直接把 account.invoice.state 改为 paid。这样只能改变页面显示,无法建立付款与发票之间的核销关系,还可能造成应收账款余额、账龄表和总账不一致。
修复前应先备份数据库,并在测试库中验证。尤其需要检查:
- 是否会重复生成会计凭证
- 是否存在部分付款
- 是否涉及贷项通知单
- 是否存在外币汇兑差额
- 是否有付款已经核销到其他发票
- 修复后的应收账款总额是否与迁移前一致
需要修复的是会计分录及其 reconciliation 关系,而不是发票状态本身。只有发票和付款的应收分录正确匹配,并且核销后的 residual 为 0,Odoo 9 才会将发票状态更新为 Paid。
备注:内容仅供参考。
