Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions base_exception/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright 2025 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

from odoo.exceptions import ValidationError


class BaseExceptionError(ValidationError):
def __init__(self, msg, rules_to_add, rules_to_remove):
super().__init__(msg)
self.rules_to_add = rules_to_add
self.rules_to_remove = rules_to_remove
1 change: 1 addition & 0 deletions base_exception/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import exception_rule
from . import base_exception_method
from . import base_exception
from . import ir_http
19 changes: 15 additions & 4 deletions base_exception/models/base_exception_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from odoo.osv import expression
from odoo.tools.safe_eval import safe_eval

from ..exceptions import BaseExceptionError

_logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -71,6 +73,7 @@ def detect_exceptions(self):
Exception ids are also written on records
"""
all_exception_ids, rules_to_remove, rules_to_add = self._get_exceptions()
# TODO: Remove outdated comment?
# Cumulate all the records to attach to the rule
# before linking. We don't want to call "rule.write()"
# which would:
Expand All @@ -84,10 +87,18 @@ def detect_exceptions(self):
# the "to remove" part generates one DELETE per rule on the relation
# table
# and the "to add" part generates one INSERT (with unnest) per rule.
for rule_id, records in rules_to_remove.items():
records.write({"exception_ids": [(3, rule_id)]})
for rule_id, records in rules_to_add.items():
records.write({"exception_ids": [(4, rule_id)]})
if rules_to_add or rules_to_remove:
raise BaseExceptionError(
"Exception on records",
{
rule_id: (records._name, records.ids)
for rule_id, records in rules_to_add.items()
},
{
rule_id: (records._name, records.ids)
for rule_id, records in rules_to_remove.items()
},
)
return all_exception_ids

@api.model
Expand Down
45 changes: 45 additions & 0 deletions base_exception/models/ir_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2025 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

from odoo import models
from odoo.api import Environment
from odoo.fields import Command
from odoo.http import request
from odoo.modules.registry import Registry

from ..exceptions import BaseExceptionError


class IrHttp(models.AbstractModel):
_inherit = "ir.http"

@classmethod
def _dispatch(cls, endpoint):
res = None
# FIXME: Find a way to condition the creation of new transaction
# only for requests that may trigger an exception rule
# ie exclude whatever goes to bus, websocket, getting views, etc
old_env = request.env
to_add = {}
to_remove = {}
with Registry(old_env.cr.dbname).cursor() as new_cr:
new_env = Environment(new_cr, old_env.uid, old_env.context)
request.env = new_env
try:
res = super()._dispatch(endpoint)
except BaseExceptionError as err:
to_add = err.rules_to_add
to_remove = err.rules_to_remove
new_env.cr.rollback()

for rule_id, (model, res_ids) in to_remove.items():
old_env[model].browse(res_ids).write(
{"exception_ids": [Command.unlink(rule_id)]}
)
for rule_id, (model, res_ids) in to_add.items():
old_env[model].browse(res_ids).write(
{"exception_ids": [Command.link(rule_id)]}
)

request.env = old_env
return res
Loading