diff --git a/confirmation_wizard/README.rst b/confirmation_wizard/README.rst new file mode 100644 index 0000000000..074fdfdf74 --- /dev/null +++ b/confirmation_wizard/README.rst @@ -0,0 +1,119 @@ +=================== +Confirmation Wizard +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:efcbab8311be7e220ab27fa46f0fcd4567d0ad071dfc40be6e5aece82b96e299 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/16.0/confirmation_wizard + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-16-0/server-ux-16-0-confirmation_wizard + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-ux&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +The module lets the developer trigger a confirmation wizard during any action in Odoo. Based on data passed to the wizard and, based on user input, executes specified methods or close wizard. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To create configuration wizard: + +Use confirm wizard which return method (return_type = method): + +.. code-block:: python + + def change_address(self, address): + if not self._context("skip_confirm", False): + # Confirmation wizard + return ( + self.env["confirmation.wizard"] + .with_context(skip_confirm=True) + .confirm_message( + _("Are you ready change partner address"), + records=self.env["res.partner"].browse(1), # One or more records + title="Confirm", + method="change_address", + callback_params={"address": address} + ) + ) + ... # Your code here + +Use confirm wizard which return window close (return_type = window_close): + +.. code-block:: python + + def method(self): + ... + return ( + self.env["confirmation.wizard"] + .with_context(invisible_cancel=True) + .confirm_no_action_message( + message="Message", + title="Notification" + ) + ) + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Cetmix + +Contributors +~~~~~~~~~~~~ + +* `Cetmix `_: + * Ivan Sokolov + * Mikhail Lapin + * Maksim Shurupov + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/confirmation_wizard/__init__.py b/confirmation_wizard/__init__.py new file mode 100644 index 0000000000..40272379f7 --- /dev/null +++ b/confirmation_wizard/__init__.py @@ -0,0 +1 @@ +from . import wizard diff --git a/confirmation_wizard/__manifest__.py b/confirmation_wizard/__manifest__.py new file mode 100644 index 0000000000..d177ac2335 --- /dev/null +++ b/confirmation_wizard/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright (C) 2024 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Confirmation Wizard", + "summary": """ + This module adds a confirmation wizard that can be called with code. + It does nothing by itself. + """, + "version": "16.0.1.0.0", + "category": "Tools", + "website": "https://github.com/OCA/server-ux", + "author": "Cetmix, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + "wizard/confirmation_wizard.xml", + ], +} diff --git a/confirmation_wizard/readme/CONTRIBUTORS.rst b/confirmation_wizard/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..ec9c0d4933 --- /dev/null +++ b/confirmation_wizard/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Cetmix `_: + * Ivan Sokolov + * Mikhail Lapin + * Maksim Shurupov diff --git a/confirmation_wizard/readme/DESCRIPTION.rst b/confirmation_wizard/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..e905a2d978 --- /dev/null +++ b/confirmation_wizard/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +The module lets the developer trigger a confirmation wizard during any action in Odoo. Based on data passed to the wizard and, based on user input, executes specified methods or close wizard. diff --git a/confirmation_wizard/readme/USAGE.rst b/confirmation_wizard/readme/USAGE.rst new file mode 100644 index 0000000000..f85c5b5e60 --- /dev/null +++ b/confirmation_wizard/readme/USAGE.rst @@ -0,0 +1,36 @@ +To create configuration wizard: + +Use confirm wizard which return method (return_type = method): + +.. code-block:: python + + def change_address(self, address): + if not self._context("skip_confirm", False): + # Confirmation wizard + return ( + self.env["confirmation.wizard"] + .with_context(skip_confirm=True) + .confirm_message( + _("Are you ready change partner address"), + records=self.env["res.partner"].browse(1), # One or more records + title="Confirm", + method="change_address", + callback_params={"address": address} + ) + ) + ... # Your code here + +Use confirm wizard which return window close (return_type = window_close): + +.. code-block:: python + + def method(self): + ... + return ( + self.env["confirmation.wizard"] + .with_context(invisible_cancel=True) + .confirm_no_action_message( + message="Message", + title="Notification" + ) + ) diff --git a/confirmation_wizard/security/ir.model.access.csv b/confirmation_wizard/security/ir.model.access.csv new file mode 100644 index 0000000000..ad6d0938e6 --- /dev/null +++ b/confirmation_wizard/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_confirmation_wizard,access.confirmation.wizard,model_confirmation_wizard,base.group_system,1,1,1,1 diff --git a/confirmation_wizard/static/description/index.html b/confirmation_wizard/static/description/index.html new file mode 100644 index 0000000000..24ace3ad77 --- /dev/null +++ b/confirmation_wizard/static/description/index.html @@ -0,0 +1,465 @@ + + + + + +Confirmation Wizard + + + +
+

Confirmation Wizard

+ + +

Beta License: AGPL-3 OCA/server-ux Translate me on Weblate Try me on Runboat

+

The module lets the developer trigger a confirmation wizard during any action in Odoo. Based on data passed to the wizard and, based on user input, executes specified methods or close wizard.

+

Table of contents

+ +
+

Usage

+

To create configuration wizard:

+

Use confirm wizard which return method (return_type = method):

+
+def change_address(self, address):
+  if not self._context("skip_confirm", False):
+    # Confirmation wizard
+    return (
+      self.env["confirmation.wizard"]
+      .with_context(skip_confirm=True)
+      .confirm_message(
+        _("Are you ready change partner address"),
+        records=self.env["res.partner"].browse(1), # One or more records
+        title="Confirm",
+        method="change_address",
+        callback_params={"address": address}
+      )
+    )
+  ... # Your code here
+
+

Use confirm wizard which return window close (return_type = window_close):

+
+def method(self):
+  ...
+  return (
+    self.env["confirmation.wizard"]
+    .with_context(invisible_cancel=True)
+    .confirm_no_action_message(
+      message="Message",
+      title="Notification"
+    )
+  )
+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Cetmix
  • +
+
+
+

Contributors

+
    +
  • +
    Cetmix:
    +
      +
    • Ivan Sokolov
    • +
    • Mikhail Lapin
    • +
    • Maksim Shurupov
    • +
    +
    +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-ux project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/confirmation_wizard/tests/__init__.py b/confirmation_wizard/tests/__init__.py new file mode 100644 index 0000000000..475c437c8b --- /dev/null +++ b/confirmation_wizard/tests/__init__.py @@ -0,0 +1 @@ +from . import test_confirmation_wizard diff --git a/confirmation_wizard/tests/test_confirmation_wizard.py b/confirmation_wizard/tests/test_confirmation_wizard.py new file mode 100644 index 0000000000..17482d24bc --- /dev/null +++ b/confirmation_wizard/tests/test_confirmation_wizard.py @@ -0,0 +1,99 @@ +from odoo.exceptions import UserError +from odoo.tests import TransactionCase + + +class TestConfirmationWizard(TransactionCase): + def setUp(self): + super().setUp() + self.partner = self.env["res.partner"].create( + { + "name": "Test Partner", + } + ) + + def test_confirm_message(self): + """Test flow when create confirmation wizard with method""" + confirmation_wizard_obj = self.env["confirmation.wizard"] + + # Default behavior + action = confirmation_wizard_obj.confirm_message( + "Message Test", + self.partner, + ) + excepted_action = self.env["ir.actions.actions"]._for_xml_id( + "confirmation_wizard.confirmation_wizard_action" + ) + excepted_action["context"] = self.env.context + self.assertDictEqual(action, excepted_action, "Dicts must be the same") + + # Wizard with title and context + excepted_title = "Confirm Wizard" + action = confirmation_wizard_obj.with_context( + invisible_cancel=True + ).confirm_message("Message Test", self.partner, title=excepted_title) + self.assertEqual(action["name"], excepted_title, "Title must be the same") + self.assertTrue( + action["context"]["invisible_cancel"], "Invisible Cancel must be True" + ) + + def test_confirm_no_action_message(self): + """Test flow when create confirmation wizard with window close""" + confirmation_wizard_obj = self.env["confirmation.wizard"] + + excepted_title = "Confirm Wizard" + action = confirmation_wizard_obj.confirm_message( + "Message Test", self.partner, title=excepted_title + ) + self.assertEqual(action["name"], excepted_title, "Title must be the same") + + def test_action_confirm_method(self): + """Test flow when confirm wizard with return type method""" + vals = { + "message": "Message Test", + "res_ids": "", + "return_type": "method", + "res_model": "res.partner", + "callback_method": "", + "callback_params": {}, + } + wizard = self.env["confirmation.wizard"].create(vals) + with self.assertRaises(UserError) as e: + wizard.action_confirm() + self.assertEqual( + str(e.exception), "Records (IDS: '') not found in model 'res.partner'." + ) + + vals1 = {**vals, "res_ids": ",".join(map(str, self.partner.ids))} + wizard = self.env["confirmation.wizard"].create(vals1) + with self.assertRaises(UserError) as e: + wizard.action_confirm() + self.assertEqual( + str(e.exception), "Method '' is not found on model 'res.partner'." + ) + + vals2 = { + **vals1, + "callback_method": "write", + "callback_params": {"vals": {"name": "New Partner #1"}}, + } + wizard = self.env["confirmation.wizard"].create(vals2) + result = wizard.action_confirm() + self.assertTrue(result, "Result must be True") + self.assertEqual( + self.partner.name, + "New Partner #1", + "Partner name must be equal to 'New Partner #1'", + ) + + def test_action_confirm_window_close(self): + """Test flow when confirm wizard with return type window close""" + wizard = self.env["confirmation.wizard"].create( + { + "message": "Message Confirmation Text", + "return_type": "window_close", + } + ) + result = wizard.action_confirm() + self.assertDictEqual( + result, {"type": "ir.actions.act_window_close"}, "Dicts must be the same" + ) diff --git a/confirmation_wizard/wizard/__init__.py b/confirmation_wizard/wizard/__init__.py new file mode 100644 index 0000000000..e81aedd783 --- /dev/null +++ b/confirmation_wizard/wizard/__init__.py @@ -0,0 +1 @@ +from . import confirmation_wizard diff --git a/confirmation_wizard/wizard/confirmation_wizard.py b/confirmation_wizard/wizard/confirmation_wizard.py new file mode 100644 index 0000000000..361377de3d --- /dev/null +++ b/confirmation_wizard/wizard/confirmation_wizard.py @@ -0,0 +1,112 @@ +# Copyright (C) 2024 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from ast import literal_eval + +from odoo import _, api, fields, models + + +class ConfirmationWizard(models.TransientModel): + _name = "confirmation.wizard" + _description = "Confirmation Wizard" + + message = fields.Char(string="Confirm Message", required=True) + + res_ids = fields.Char() + res_model = fields.Char() + + callback_method = fields.Char() + callback_params = fields.Json() + + return_type = fields.Selection( + [ + ("window_close", "Return Window Close Action"), + ("method", "Return Method"), + ], + default="window_close", + required=True, + ) + + @api.model + def _prepare_action(self, title=None): + """ + Prepare confirmation wizard + :param title: wizard title + """ + action = self.env["ir.actions.act_window"]._for_xml_id( + "confirmation_wizard.confirmation_wizard_action" + ) + action.update( + { + "name": title or _("Confirmation"), + "res_id": self.id, + "context": self._context, + } + ) + return action + + @api.model + def confirm_message( + self, message, records, title=None, method=None, callback_params=None + ): + """ + Confirm message with method return type + :param message: confirmation message + :param records: record set + :param title: wizard title + :param method: triggered method + :param callback_params: method arguments + :return dict: ir actions act window dict + """ + wizard = self.create( + { + "message": message, + "res_ids": repr(records.ids), + "return_type": "method", + "res_model": records._name, + "callback_method": method, + "callback_params": callback_params or {}, + } + ) + return wizard._prepare_action(title) + + @api.model + def confirm_no_action_message(self, message, title=None): + """ + Confirm message with close window return type + :param message: confirmation message + :param title: wizard title + :return dict: ir actions act window dict + """ + wizard = self.create( + { + "message": message, + "return_type": "window_close", + } + ) + return wizard._prepare_action(title) + + def _confirm_window_close(self): + """Action confirm for return type window close""" + return {"type": "ir.actions.act_window_close"} + + def _confirm_method(self): + """Action confirm for return type method""" + res_ids = literal_eval(self.res_ids) if self.res_ids else [] + records = self.env[self.res_model].browse(res_ids) + if not records.exists(): + raise models.UserError( + _("Records (IDS: '%(ids)s') not found in model '%(model)s'.") + % {"ids": self.res_ids, "model": self.res_model} + ) + if not hasattr(records, self.callback_method): + raise models.UserError( + _("Method '%(callback_method)s' is not found on model '%(res_model)s'.") + % {"callback_method": self.callback_method, "res_model": self.res_model} + ) + params = self.callback_params or {} + return getattr(records, self.callback_method)(**params) + + def action_confirm(self): + """Action confirm wizard""" + method = getattr(self, f"_confirm_{self.return_type}", None) + return method() diff --git a/confirmation_wizard/wizard/confirmation_wizard.xml b/confirmation_wizard/wizard/confirmation_wizard.xml new file mode 100644 index 0000000000..12a9b2e67a --- /dev/null +++ b/confirmation_wizard/wizard/confirmation_wizard.xml @@ -0,0 +1,38 @@ + + + + + confirmation.wizard.form.view + confirmation.wizard + +
+ +
+
+ +
+
+ + + Confirmation + ir.actions.act_window + confirmation.wizard + form + + new + + +
diff --git a/setup/confirmation_wizard/odoo/addons/confirmation_wizard b/setup/confirmation_wizard/odoo/addons/confirmation_wizard new file mode 120000 index 0000000000..aa9ecabff0 --- /dev/null +++ b/setup/confirmation_wizard/odoo/addons/confirmation_wizard @@ -0,0 +1 @@ +../../../../confirmation_wizard \ No newline at end of file diff --git a/setup/confirmation_wizard/setup.py b/setup/confirmation_wizard/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/confirmation_wizard/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)