# -*- coding: utf-8 -*- # Copyright 2025-2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # # FIX: account_followup never clears followup_next_action_date # ------------------------------------------------------------ # Odoo's account_followup module sets followup_next_action_date on # res.partner when overdue invoices are detected, but never clears it # when those invoices are paid. The daily cron re-evaluates status # via SQL so it *usually* skips zero-balance partners, but the stale # field causes partners to appear in follow-up lists and manual sends # can still fire emails to fully-paid clients. # # This override hooks into _invoice_paid_hook (called by the core # reconciliation engine) and clears the follow-up date when the # partner has no remaining receivable balance. import logging from odoo import models _logger = logging.getLogger(__name__) class AccountMove(models.Model): _inherit = 'account.move' def _invoice_paid_hook(self): super()._invoice_paid_hook() partner_model = self.env['res.partner'] if 'followup_next_action_date' not in partner_model._fields: return partners = self.mapped('partner_id.commercial_partner_id').filtered( 'followup_next_action_date' ) if not partners: return for partner in partners: has_balance = self.env['account.move.line'].search_count([ ('partner_id', '=', partner.id), ('account_id.account_type', '=', 'asset_receivable'), ('parent_state', '=', 'posted'), ('reconciled', '=', False), ('amount_residual', '>', 0), ], limit=1) if not has_balance: partner.write({'followup_next_action_date': False}) _logger.info( "Cleared follow-up for partner %s (ID %s) -- no outstanding balance", partner.name, partner.id, )