from odoo import api, fields, models, _ class AccountPayment(models.Model): _inherit = 'account.payment' # Computed residual amount field payment_residual = fields.Monetary( string='Payment Residual', compute='_compute_payment_residual', currency_field='currency_id', help="Residual amount of this payment (amount not yet reconciled)", readonly=True ) payment_residual_currency = fields.Monetary( string='Payment Residual Currency', compute='_compute_payment_residual', currency_field='currency_id', help="Residual amount in payment currency", readonly=True ) @api.depends('move_id.line_ids.amount_residual', 'move_id.line_ids.amount_residual_currency', 'move_id.line_ids.account_id', 'move_id.line_ids.account_id.account_type', 'is_matched') def _compute_payment_residual(self): """Compute the residual amount for payments. Matched payments always display zero residual. For unmatched payments, we first look at residuals on reconcilable counterpart lines (receivable/payable/write-off). If those are fully cleared, we fall back to the liquidity lines to surface outstanding balances waiting for bank statement matching. """ for pay in self: if pay.is_matched or not pay.move_id: pay.payment_residual = 0.0 pay.payment_residual_currency = 0.0 continue company_currency = pay.company_id.currency_id payment_currency = pay.currency_id or company_currency liquidity_lines, counterpart_lines, writeoff_lines = pay._seek_for_lines() reconcilable_lines = (counterpart_lines + writeoff_lines).filtered(lambda l: l.account_id.reconcile) residual = sum(reconcilable_lines.mapped('amount_residual')) residual_currency = sum( reconcilable_lines.mapped( 'amount_residual_currency' if payment_currency != company_currency else 'amount_residual' ) ) liquidity_residual = sum(liquidity_lines.mapped('amount_residual')) liquidity_residual_currency = sum( liquidity_lines.mapped( 'amount_residual_currency' if payment_currency != company_currency else 'amount_residual' ) ) if not company_currency.is_zero(liquidity_residual): residual = liquidity_residual if not payment_currency.is_zero(liquidity_residual_currency): residual_currency = liquidity_residual_currency pay.payment_residual = abs(residual) pay.payment_residual_currency = abs(residual_currency)