from odoo import fields, models, api, _ from datetime import date class Partner(models.Model): _inherit = "res.partner" postpone_hold_until = fields.Date( string="Postpone Hold", help="Grace period specific to this partner despite unpaid invoices.", tracking=True, ) hold_bg = fields.Boolean( string="Hold (technical)", compute="_compute_hold_bg", store=True, default=False, compute_sudo=True, tracking=True, ) on_hold = fields.Boolean( string="Account on Hold", help="Client account is on hold for unpaid overdue invoices.", compute="_compute_on_hold", compute_sudo=True, ) @api.depends("postpone_hold_until", "hold_bg") def _compute_on_hold(self): # manually re-compute hold_bg since followup_status doesn't get updated in Python but gets recalculated # by an SQL query every time self._compute_hold_bg() for rec in self: # If the parent company is on hold, so are all its sub-contacts and subsidiaries if rec.commercial_partner_id and rec.commercial_partner_id.on_hold: rec.on_hold = True return # If there is no parent company or the parent is not on hold, we compute for ourselves if rec.hold_bg and not ( rec.postpone_hold_until and rec.postpone_hold_until > date.today() ): rec.on_hold = True else: if rec.on_hold: rec.message_post(_("Credit hold lifted.")) rec.on_hold = False @api.autovacuum def _cleanup_expired_hold_postponements(self): expired_holds = self.search([("postpone_hold_until", "<=", date.today())]) expired_holds.write({"postpone_hold_until": False}) def action_credit_hold(self): for rec in self: rec.hold_bg = True rec.message_post(body=_("Placed on credit hold.")) def action_lift_credit_hold(self): for rec in self: rec.hold_bg = False rec.message_post(body=_("Credit hold lifted.")) def _execute_followup_partner(self, options=None): res = super()._execute_followup_partner(options) if self.followup_status == "in_need_of_action": if self.followup_line_id.account_hold: self.action_credit_hold() return res @api.depends("followup_status", "followup_line_id") def _compute_hold_bg(self): first_followup_level = self._get_first_followup_level() for rec in self: prev_hold_bg = rec.hold_bg level = rec.followup_line_id if rec.followup_status == "no_action_needed" and not level: rec.hold_bg = False else: rec.hold_bg = prev_hold_bg