# Copyright 2011 Raphaƫl Valyi, Renato Lima, Guewen Baconnier, Sodexis # Copyright 2017 Akretion (http://www.akretion.com) # Mourad EL HADJ MIMOUNE # Copyright 2020 Hibou Corp. # Copyright 2023 ACSONE SA/NV (http://acsone.eu) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import html import logging from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError _logger = logging.getLogger(__name__) class BaseExceptionModel(models.AbstractModel): _inherit = "base.exception.method" _name = "base.exception" _order = "main_exception_id asc" _description = "Exception" main_exception_id = fields.Many2one( "exception.rule", compute="_compute_main_error", string="Main Exception", store=True, ) exceptions_summary = fields.Html(compute="_compute_exceptions_summary") exception_ids = fields.Many2many("exception.rule", string="Exceptions", copy=False) ignore_exception = fields.Boolean("Ignore Exceptions", copy=False) def action_ignore_exceptions(self): if any(self.exception_ids.mapped("is_blocking")): raise UserError( _( "The exceptions can not be ignored, because " "some of them are blocking." ) ) self.write({"ignore_exception": True}) return True @api.depends("exception_ids", "ignore_exception") def _compute_main_error(self): for rec in self: if not rec.ignore_exception and rec.exception_ids: rec.main_exception_id = rec.exception_ids[0] else: rec.main_exception_id = False @api.depends("exception_ids", "ignore_exception") def _compute_exceptions_summary(self): for rec in self: if rec.exception_ids and not rec.ignore_exception: rec.exceptions_summary = "" % "".join( [ "
  • %s: %s %s
  • " % tuple( map( html.escape, ( e.name, e.description or "", _("(Blocking exception)") if e.is_blocking else "", ), ) ) for e in rec.exception_ids ] ) else: rec.exceptions_summary = False def _popup_exceptions(self): """This method is used to show the popup action view. Used in several dependent modules.""" record = self._get_popup_action() action = record.sudo().read()[0] action = { field: value for field, value in action.items() if field in record._get_readable_fields() } action.update( { "context": { "active_id": self.ids[0], "active_ids": self.ids, "active_model": self._name, } } ) return action @api.model def _get_popup_action(self): return self.env.ref("base_exception.action_exception_rule_confirm") def _check_exception(self): """Check exceptions This method must be used in a constraint that must be created in the object that inherits for base.exception. .. code-block:: python @api.constrains("ignore_exception") def sale_check_exception(self): # ... self._check_exception() For convenience, this check can be skipped by setting check_exception=False in context. Exceptions will be raised as ValidationError, but this can be disabled by setting raise_exception=False in context. They will still be detected and updated on the related record, though. """ if not self.env.context.get("check_exception", True): # pragma: no cover return True exception_ids = self.detect_exceptions() if exception_ids and self.env.context.get("raise_exception", True): exceptions = self.env["exception.rule"].browse(exception_ids) raise ValidationError("\n".join(exceptions.mapped("name")))