from odoo import api, models, fields from odoo.tools.translate import _ class AccountMove(models.Model): _inherit = 'account.move' @api.depends('amount_residual', 'move_type', 'state', 'company_id') def _compute_payment_state(self): super()._compute_payment_state() for invoice in self: if invoice.payment_state == 'paid' and invoice.is_invoice(include_receipts=True): term_lines = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term' and l.account_id.account_type == 'liability_current') if term_lines and invoice.currency_id.is_zero(invoice.amount_residual): payments = (term_lines.matched_debit_ids.debit_move_id.payment_id | term_lines.matched_credit_ids.credit_move_id.payment_id) if any(not p.is_matched for p in payments if p): invoice.payment_state = invoice._get_invoice_in_payment_state() def _get_all_reconciled_invoice_partials(self): reconciled_partials = super()._get_all_reconciled_invoice_partials() if isinstance(reconciled_partials, dict): reconciled_partials = [] self.ensure_one() # If the super() returns something, it might only be for payable/receivable. # We also need to fetch partials for liability_current reconciled_lines = self.line_ids.filtered(lambda line: line.account_id.account_type == 'liability_current') if not reconciled_lines: return reconciled_partials self.env['account.partial.reconcile'].flush_model([ 'credit_amount_currency', 'credit_move_id', 'debit_amount_currency', 'debit_move_id', 'exchange_move_id', ]) query = ''' SELECT part.id, part.exchange_move_id, part.debit_amount_currency AS amount, part.credit_move_id AS aml_id, 'debit' AS direction, part.create_date FROM account_partial_reconcile part WHERE part.debit_move_id IN %s UNION ALL SELECT part.id, part.exchange_move_id, part.credit_amount_currency AS amount, part.debit_move_id AS aml_id, 'credit' AS direction, part.create_date FROM account_partial_reconcile part WHERE part.credit_move_id IN %s ORDER BY create_date DESC ''' self.env.cr.execute(query, [tuple(reconciled_lines.ids), tuple(reconciled_lines.ids)]) aml_ids = set() exchange_move_ids = set() partials_data = [] for row in self.env.cr.dictfetchall(): aml_ids.add(row['aml_id']) if row['exchange_move_id']: exchange_move_ids.add(row['exchange_move_id']) partials_data.append(row) if not partials_data: return reconciled_partials amls = self.env['account.move.line'].browse(list(aml_ids)).filtered(lambda aml: aml.move_id.id not in exchange_move_ids) amls_dict = {aml.id: aml for aml in amls} for partial in partials_data: if partial['aml_id'] not in amls_dict: continue counterpart_aml = amls_dict[partial['aml_id']] reconciled_partials.append({ 'aml': counterpart_aml, 'amount': partial['amount'], 'currency': self.currency_id, 'is_exchange': bool(partial['exchange_move_id']), 'partial_id': partial['id'], }) return reconciled_partials def _compute_payments_widget_to_reconcile_info(self): super()._compute_payments_widget_to_reconcile_info() for move in self: if move.state != 'posted' or move.payment_state not in ('not_paid', 'partial') or not move.is_invoice(include_receipts=True): continue pay_term_lines = move.line_ids.filtered(lambda line: line.account_id.account_type == 'liability_current') if not pay_term_lines: continue domain = [ ('account_id', 'in', pay_term_lines.account_id.ids), ('parent_state', '=', 'posted'), ('partner_id', '=', move.commercial_partner_id.id), ('reconciled', '=', False), '|', ('amount_residual', '!=', 0.0), ('amount_residual_currency', '!=', 0.0), ] payments_widget_vals = move.invoice_outstanding_credits_debits_widget or {'outstanding': True, 'content': [], 'move_id': move.id} if move.is_inbound(): domain.append(('balance', '<', 0.0)) payments_widget_vals['title'] = _('Outstanding credits') else: domain.append(('balance', '>', 0.0)) payments_widget_vals['title'] = _('Outstanding debits') for line in self.env['account.move.line'].search(domain): if line.currency_id == move.currency_id: amount = abs(line.amount_residual_currency) else: amount = line.company_currency_id._convert(abs(line.amount_residual), move.currency_id, move.company_id, line.date) if move.currency_id.is_zero(amount): continue # avoid duplicates if payments_widget_vals['content'] and any(c['id'] == line.id for c in payments_widget_vals['content']): continue payments_widget_vals['content'].append({ 'journal_name': line.ref or line.move_id.name, 'amount': amount, 'currency_id': move.currency_id.id, 'id': line.id, 'move_id': line.move_id.id, 'date': fields.Date.to_string(line.date), 'account_payment_id': line.payment_id.id, }) if payments_widget_vals['content']: move.invoice_outstanding_credits_debits_widget = payments_widget_vals move.invoice_has_outstanding = True