diff --git a/addons/base_automation/i18n/base_automation.pot b/addons/base_automation/i18n/base_automation.pot index 6ffef9642c9..1fd7eb6f1f4 100644 --- a/addons/base_automation/i18n/base_automation.pot +++ b/addons/base_automation/i18n/base_automation.pot @@ -15,6 +15,15 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "" +"\"\n" +" (ID:" +msgstr "" + #. module: base_automation #: model:ir.model.fields,field_description:base_automation.field_base_automation__help msgid "Action Description" @@ -174,6 +183,24 @@ msgstr "" msgid "Delay type" msgstr "" +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "Disable Action" +msgstr "" + +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "" +"Disabling this automated action will enable you to continue your workflow\n" +" but any data created after this could potentially be corrupted,\n" +" as you are effectively disabling a customization that may set\n" +" important and/or required fields." +msgstr "" + #. module: base_automation #: model:ir.model.fields,field_description:base_automation.field_base_automation__display_name msgid "Display Name" @@ -189,6 +216,13 @@ msgstr "" msgid "Due type" msgstr "" +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "Edit action" +msgstr "" + #. module: base_automation #: model:ir.model.fields,field_description:base_automation.field_base_automation__template_id msgid "Email Template" @@ -424,6 +458,15 @@ msgid "" "updated.If empty, all fields are watched." msgstr "" +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "" +"The error occurred during the execution of the automated action\n" +" \"" +msgstr "" + #. module: base_automation #: model:ir.model.fields,field_description:base_automation.field_base_automation__trigger msgid "Trigger" @@ -530,3 +573,18 @@ msgid "" "Write Python code that the action will execute. Some variables are available" " for use; help about python expression is given in the help tab." msgstr "" + +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "" +"You can ask an administrator to disable or correct this automated action." +msgstr "" + +#. module: base_automation +#. openerp-web +#: code:addons/base_automation/static/src/xml/base_automation_error_dialog.xml:0 +#, python-format +msgid "You can disable this automated action or edit it to solve the issue." +msgstr "" diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 17be94a5664..e587db98996 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -209,6 +209,16 @@ class BaseAutomation(models.Model): else: return records, None + @api.model + def _add_postmortem_action(self, e): + if self.user_has_groups('base.group_user'): + e.context = {} + e.context['exception_class'] = 'base_automation' + e.context['base_automation'] = { + 'id': self.id, + 'name': self.name, + } + def _process(self, records, domain_post=None): """ Process action ``self`` on the ``records`` that have not been done yet. """ # filter out the records on which self has already been done @@ -242,7 +252,11 @@ class BaseAutomation(models.Model): 'active_id': record.id, 'domain_post': domain_post, } - self.action_server_id.with_context(**ctx).run() + try: + self.action_server_id.with_context(**ctx).run() + except Exception as e: + self._add_postmortem_action(e) + raise e def _check_trigger_fields(self, record): """ Return whether any of the trigger fields has been modified on ``record``. """ @@ -367,7 +381,12 @@ class BaseAutomation(models.Model): action_rule = self.env['base.automation'].browse(action_rule_id) result = {} server_action = action_rule.action_server_id.with_context(active_model=self._name, onchange_self=self) - res = server_action.run() + try: + res = server_action.run() + except Exception as e: + action_rule._add_postmortem_action(e) + raise e + if res: if 'value' in res: res['value'].pop('id', None) diff --git a/addons/base_automation/static/src/js/base_automation_error_dialog.js b/addons/base_automation/static/src/js/base_automation_error_dialog.js new file mode 100644 index 00000000000..3fa4cf0cc06 --- /dev/null +++ b/addons/base_automation/static/src/js/base_automation_error_dialog.js @@ -0,0 +1,78 @@ +odoo.define('base_automation.BaseAutomatioErrorDialog', function (require) { + "use strict"; + + const CrashManager = require('web.CrashManager'); + const ErrorDialog = CrashManager.ErrorDialog; + const ErrorDialogRegistry = require('web.ErrorDialogRegistry'); + const session = require('web.session'); + + const BaseAutomationErrorDialog = ErrorDialog.extend({ + xmlDependencies: (ErrorDialog.prototype.xmlDependencies || []).concat( + ['/base_automation/static/src/xml/base_automation_error_dialog.xml'] + ), + template: 'CrashManager.BaseAutomationError', + events: { + 'click .o_disable_action_button': '_onDisableAction', + 'click .o_edit_action_button': '_onEditAction', + }, + /** + * Assign the `base_automation` object based on the error data, + * which is then used by the `CrashManager.BaseAutomationError` template + * and the events defined above. + * @override + * @param {Object} error + * @param {string} error.data.context.base_automation.id the ID of the failing automated action + * @param {string} error.data.context.base_automation.name the name of the failing automated action + */ + init: function (parent, options, error) { + this._super.apply(this, arguments); + this.base_automation = error.data.context.base_automation; + this.is_admin = session.is_admin; + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * This method is called when the user clicks on the 'Disable action' button + * displayed when a crash occurs in the evaluation of an automated action. + * Then, we write `active` to `False` on the automated action to disable it. + * + * @private + * @param {MouseEvent} ev + */ + _onDisableAction: function (ev) { + ev.preventDefault(); + this._rpc({ + model: 'base.automation', + method: 'write', + args: [[this.base_automation.id], { + active: false, + }], + }).then(this.destroy.bind(this)); + }, + /** + * This method is called when the user clicks on the 'Edit action' button + * displayed when a crash occurs in the evaluation of an automated action. + * Then, we redirect the user to the automated action form. + * + * @private + * @param {MouseEvent} ev + */ + _onEditAction: function (ev) { + ev.preventDefault(); + this.do_action({ + name: 'Automated Actions', + res_model: 'base.automation', + res_id: this.base_automation.id, + views: [[false, 'form']], + type: 'ir.actions.act_window', + view_mode: 'form', + }); + }, + }); + + ErrorDialogRegistry.add('base_automation', BaseAutomationErrorDialog); + +}); diff --git a/addons/base_automation/static/src/xml/base_automation_error_dialog.xml b/addons/base_automation/static/src/xml/base_automation_error_dialog.xml new file mode 100644 index 00000000000..e45c59117ec --- /dev/null +++ b/addons/base_automation/static/src/xml/base_automation_error_dialog.xml @@ -0,0 +1,36 @@ + + + + + + +

+ The error occurred during the execution of the automated action + "" + (ID: ). +
+

+

+ You can ask an administrator to disable or correct this automated action. +

+

+ You can disable this automated action or edit it to solve the issue.
+ Disabling this automated action will enable you to continue your workflow + but any data created after this could potentially be corrupted, + as you are effectively disabling a customization that may set + important and/or required fields. +

+
+
+ + + + + + +
+
diff --git a/addons/base_automation/static/tests/base_automation_error_dialog.js b/addons/base_automation/static/tests/base_automation_error_dialog.js new file mode 100644 index 00000000000..af2e9ff6d4d --- /dev/null +++ b/addons/base_automation/static/tests/base_automation_error_dialog.js @@ -0,0 +1,72 @@ +odoo.define('base_automation.BaseAutomatioErrorDialogTests', function (require) { +'use strict'; + + const CrashManager = require('web.CrashManager').CrashManager; + const session = require('web.session'); + + QUnit.module('base_automation', {}, function () { + + QUnit.module('Error Dialog'); + + QUnit.test('Error due to an automated action', async function (assert) { + assert.expect(4); + + let baseAutomationName = 'Test base automation error dialog'; + let error = { + type: 'Odoo Client Error', + message: 'Message', + data: { + debug: 'Traceback', + context: { + exception_class: 'base_automation', + base_automation: { + id: 1, + name: baseAutomationName, + }, + }, + }, + }; + // Force the user session to be admin, to display the disable and edit action buttons, + // then reset back to the origin value after the test. + let isAdmin = session.is_admin; + session.is_admin = true; + + let crashManager = new CrashManager(); + let dialog = crashManager.show_error(error); + + await dialog._opened; + + assert.containsOnce(document.body, '.modal .o_clipboard_button'); + assert.containsOnce(document.body, '.modal .o_disable_action_button'); + assert.containsOnce(document.body, '.modal .o_edit_action_button'); + assert.ok(dialog.$el.text().indexOf(baseAutomationName) !== -1); + + session.is_admin = isAdmin; + crashManager.destroy(); + }); + + QUnit.test('Error not due to an automated action', async function (assert) { + assert.expect(3); + + let error = { + type: 'Odoo Client Error', + message: 'Message', + data: { + debug: 'Traceback', + }, + }; + let crashManager = new CrashManager(); + let dialog = crashManager.show_error(error); + + await dialog._opened; + + assert.containsOnce(document.body, '.modal .o_clipboard_button'); + assert.containsNone(document.body, '.modal .o_disable_action_button'); + assert.containsNone(document.body, '.modal .o_edit_action_button'); + + crashManager.destroy(); + }); + + }); + +}); diff --git a/addons/base_automation/views/base_automation_view.xml b/addons/base_automation/views/base_automation_view.xml index e999edb9e2e..8902d8362f4 100644 --- a/addons/base_automation/views/base_automation_view.xml +++ b/addons/base_automation/views/base_automation_view.xml @@ -123,4 +123,16 @@ +