diff --git a/README.md b/README.md index 6de8cc4240..909404abaf 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ addon | version | maintainers | summary [base_substate](base_substate/) | 18.0.1.0.1 | | Base Sub State [base_technical_features](base_technical_features/) | 18.0.1.0.2 | | Access to technical features without activating debug mode [base_tier_validation](base_tier_validation/) | 18.0.3.1.2 | LoisRForgeFlow | Implement a validation process based on tiers. +[base_tier_validation_confirm_auth](base_tier_validation_confirm_auth/) | 18.0.1.0.0 | | Authentication confirmation for base tiers. [base_tier_validation_correction](base_tier_validation_correction/) | 18.0.1.0.0 | kittiu | Correct tier.review data after it has been created. [base_tier_validation_formula](base_tier_validation_formula/) | 18.0.1.0.0 | | Formulas for Base tier validation [base_tier_validation_forward](base_tier_validation_forward/) | 18.0.2.0.1 | kittiu | Forward option for base tiers diff --git a/base_tier_validation_confirm_auth/README.rst b/base_tier_validation_confirm_auth/README.rst new file mode 100644 index 0000000000..109d86c10d --- /dev/null +++ b/base_tier_validation_confirm_auth/README.rst @@ -0,0 +1,91 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Base Tier Validation Confirm Auth +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:0ddbbecda7996075b55be022b81821bfe09ed00649279f7cdb2a8297227e901a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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/18.0/base_tier_validation_confirm_auth + :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-18-0/server-ux-18-0-base_tier_validation_confirm_auth + :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=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Allows to add an authentication check to tier validation, where the user +must confirm his authentication in order to approve or reject the tier +review. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +1. Go to *Settings > Technical > Tier Validations > Tier Definition > + Select one tier definition*. +2. Mark "Require Authentication" field. + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Arnau Cruz arnau.cruz@forgeflow.com + +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/base_tier_validation_confirm_auth/__init__.py b/base_tier_validation_confirm_auth/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/base_tier_validation_confirm_auth/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/base_tier_validation_confirm_auth/__manifest__.py b/base_tier_validation_confirm_auth/__manifest__.py new file mode 100644 index 0000000000..5783b8b033 --- /dev/null +++ b/base_tier_validation_confirm_auth/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Base Tier Validation Confirm Auth", + "summary": "Authentication confirmation for base tiers.", + "version": "18.0.1.0.0", + "category": "Tools", + "website": "https://github.com/OCA/server-ux", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["base_tier_validation"], + "data": [ + "views/tier_definition_view.xml", + ], + "application": False, + "installable": True, +} diff --git a/base_tier_validation_confirm_auth/i18n/base_tier_validation_confirm_auth.pot b/base_tier_validation_confirm_auth/i18n/base_tier_validation_confirm_auth.pot new file mode 100644 index 0000000000..d5254235dc --- /dev/null +++ b/base_tier_validation_confirm_auth/i18n/base_tier_validation_confirm_auth.pot @@ -0,0 +1,61 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_tier_validation_confirm_auth +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model,name:base_tier_validation_confirm_auth.model_comment_wizard +msgid "Comment Wizard" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model.fields,help:base_tier_validation_confirm_auth.field_tier_definition__require_authentication +#: model:ir.model.fields,help:base_tier_validation_confirm_auth.field_tier_review__require_authentication +msgid "" +"If enabled, the user will be asked to authenticate himself in order to " +"validate or reject the tier." +msgstr "" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model.fields,field_description:base_tier_validation_confirm_auth.field_tier_definition__require_authentication +#: model:ir.model.fields,field_description:base_tier_validation_confirm_auth.field_tier_review__require_authentication +#: model:ir.model.fields,field_description:base_tier_validation_confirm_auth.field_tier_validation__require_authentication +msgid "Require Authentication" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#. odoo-python +#: code:addons/base_tier_validation_confirm_auth/models/res_users.py:0 +msgid "Security Control" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#. odoo-python +#: code:addons/base_tier_validation_confirm_auth/models/res_users.py:0 +msgid "This method can only be accessed over HTTP" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model,name:base_tier_validation_confirm_auth.model_tier_definition +msgid "Tier Definition" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model,name:base_tier_validation_confirm_auth.model_tier_review +msgid "Tier Review" +msgstr "" + +#. module: base_tier_validation_confirm_auth +#: model:ir.model,name:base_tier_validation_confirm_auth.model_tier_validation +msgid "Tier Validation (abstract)" +msgstr "" diff --git a/base_tier_validation_confirm_auth/models/__init__.py b/base_tier_validation_confirm_auth/models/__init__.py new file mode 100644 index 0000000000..d4e0af9ca8 --- /dev/null +++ b/base_tier_validation_confirm_auth/models/__init__.py @@ -0,0 +1,4 @@ +from . import tier_definition +from . import tier_review +from . import tier_validation +from . import res_users diff --git a/base_tier_validation_confirm_auth/models/res_users.py b/base_tier_validation_confirm_auth/models/res_users.py new file mode 100644 index 0000000000..526d410937 --- /dev/null +++ b/base_tier_validation_confirm_auth/models/res_users.py @@ -0,0 +1,57 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import json +from functools import wraps + +from odoo import _ +from odoo.exceptions import UserError +from odoo.http import request + + +def _jsonable(o): + try: + json.dumps(o) + except TypeError: + return False + else: + return True + + +def check_authentication(fn): + @wraps(fn) + def wrapped(self, *args, **kwargs): + if not request: + raise UserError(_("This method can only be accessed over HTTP")) + + if self.env.context.get("identity_checked"): + return fn(self, *args, **kwargs) + + ctx = self.env.context.copy() + ctx["identity_checked"] = True + + safe_context = {k: v for k, v in ctx.items() if _jsonable(v)} + + w = ( + self.sudo() + .env["res.users.identitycheck"] + .create( + { + "request": json.dumps( + [safe_context, self._name, self.ids, fn.__name__, args, kwargs] + ) + } + ) + ) + + return { + "type": "ir.actions.act_window", + "res_model": "res.users.identitycheck", + "res_id": w.id, + "name": _("Security Control"), + "target": "new", + "views": [(False, "form")], + } + + wrapped.__has_check_identity = True + return wrapped diff --git a/base_tier_validation_confirm_auth/models/tier_definition.py b/base_tier_validation_confirm_auth/models/tier_definition.py new file mode 100644 index 0000000000..67dc5334cf --- /dev/null +++ b/base_tier_validation_confirm_auth/models/tier_definition.py @@ -0,0 +1,14 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class TierDefinition(models.Model): + _inherit = "tier.definition" + + require_authentication = fields.Boolean( + help="If enabled, the user will be asked to authenticate " + "himself in order to validate or reject the tier.", + default=False, + ) diff --git a/base_tier_validation_confirm_auth/models/tier_review.py b/base_tier_validation_confirm_auth/models/tier_review.py new file mode 100644 index 0000000000..fe8f0d849c --- /dev/null +++ b/base_tier_validation_confirm_auth/models/tier_review.py @@ -0,0 +1,12 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class TierReview(models.Model): + _inherit = "tier.review" + + require_authentication = fields.Boolean( + related="definition_id.require_authentication", readonly=True + ) diff --git a/base_tier_validation_confirm_auth/models/tier_validation.py b/base_tier_validation_confirm_auth/models/tier_validation.py new file mode 100644 index 0000000000..9caea440dc --- /dev/null +++ b/base_tier_validation_confirm_auth/models/tier_validation.py @@ -0,0 +1,40 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + +from .res_users import check_authentication + + +class TierValidation(models.AbstractModel): + _inherit = "tier.validation" + + require_authentication = fields.Boolean(compute="_compute_require_authentication") + + def _compute_require_authentication(self): + for rec in self: + require_authentication = rec.review_ids.filtered( + lambda r: r.status in ("waiting", "pending") + and (self.env.user in r.reviewer_ids) + ).mapped("require_authentication") + rec.require_authentication = True in require_authentication + + def validate_tier(self): + self.ensure_one() + if not self.has_comment and self.require_authentication: + return self._validate_tier_with_identity_check() + return super().validate_tier() + + def reject_tier(self): + self.ensure_one() + if not self.has_comment and self.require_authentication: + return self._reject_tier_with_identity_check() + return super().reject_tier() + + @check_authentication + def _validate_tier_with_identity_check(self): + return super().validate_tier() + + @check_authentication + def _reject_tier_with_identity_check(self): + return super().reject_tier() diff --git a/base_tier_validation_confirm_auth/pyproject.toml b/base_tier_validation_confirm_auth/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/base_tier_validation_confirm_auth/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_tier_validation_confirm_auth/readme/CONFIGURE.md b/base_tier_validation_confirm_auth/readme/CONFIGURE.md new file mode 100644 index 0000000000..887a1cfbac --- /dev/null +++ b/base_tier_validation_confirm_auth/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +To configure this module, you need to: + +1. Go to *Settings \> Technical \> Tier Validations \> Tier + Definition \> Select one tier definition*. +2. Mark "Require Authentication" field. diff --git a/base_tier_validation_confirm_auth/readme/CONTRIBUTORS.md b/base_tier_validation_confirm_auth/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..629a347ae8 --- /dev/null +++ b/base_tier_validation_confirm_auth/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Arnau Cruz diff --git a/base_tier_validation_confirm_auth/readme/DESCRIPTION.md b/base_tier_validation_confirm_auth/readme/DESCRIPTION.md new file mode 100644 index 0000000000..18c2248d8c --- /dev/null +++ b/base_tier_validation_confirm_auth/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +Allows to add an authentication check to tier validation, +where the user must confirm his authentication in order to approve or reject the tier review. diff --git a/base_tier_validation_confirm_auth/static/description/icon.png b/base_tier_validation_confirm_auth/static/description/icon.png new file mode 100644 index 0000000000..1dcc49c24f Binary files /dev/null and b/base_tier_validation_confirm_auth/static/description/icon.png differ diff --git a/base_tier_validation_confirm_auth/static/description/index.html b/base_tier_validation_confirm_auth/static/description/index.html new file mode 100644 index 0000000000..8142946455 --- /dev/null +++ b/base_tier_validation_confirm_auth/static/description/index.html @@ -0,0 +1,441 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Base Tier Validation Confirm Auth

+ +

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

+

Allows to add an authentication check to tier validation, where the user +must confirm his authentication in order to approve or reject the tier +review.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  1. Go to Settings > Technical > Tier Validations > Tier Definition > +Select one tier definition.
  2. +
  3. Mark “Require Authentication” field.
  4. +
+
+
+

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

+
    +
  • ForgeFlow
  • +
+
+ +
+

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/base_tier_validation_confirm_auth/tests/__init__.py b/base_tier_validation_confirm_auth/tests/__init__.py new file mode 100644 index 0000000000..2cab50552b --- /dev/null +++ b/base_tier_validation_confirm_auth/tests/__init__.py @@ -0,0 +1 @@ +from . import test_tier_validation_authentication_confirm diff --git a/base_tier_validation_confirm_auth/tests/test_tier_validation_authentication_confirm.py b/base_tier_validation_confirm_auth/tests/test_tier_validation_authentication_confirm.py new file mode 100644 index 0000000000..ee40527345 --- /dev/null +++ b/base_tier_validation_confirm_auth/tests/test_tier_validation_authentication_confirm.py @@ -0,0 +1,250 @@ +# Copyright 2026 ForgeFlow S.L. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from unittest.mock import MagicMock, patch + +from odoo.tests import Form, tagged + +from odoo.addons.base_tier_validation.tests.common import CommonTierValidation + + +@tagged("post_install", "-at_install") +class TierTierValidationAuthenticationConfirm(CommonTierValidation): + @classmethod + def setUpClass(cls): + super().setUpClass() + + def test_01_authentication_confirmation_and_comment(self): + # Set user password for validation + self.test_user_1.password = "test_user_1" + + # Create new test record + test_record = self.test_model.create({"test_field": 2.5}) + + # Create tier definitions + self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "has_comment": True, + "require_authentication": True, + } + ) + # Request validation + review = test_record.with_user(self.test_user_2.id).request_validation() + self.assertTrue(review) + + # Let _compute_can_review assign status 'pending' instead of waiting + review.flush_recordset() + record = test_record.with_user(self.test_user_1.id) + res = record.validate_tier() + ctx = res.get("context") + wizard = Form(self.env["comment.wizard"].with_context(**ctx)) + wizard.comment = "Test Comment" + wiz = wizard.save() + + # Mock requests to avoid errors outside HTTP request + with patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ): + res = wiz.add_comment() + + self.assertEqual(res["res_model"], "res.users.identitycheck") + identity_wiz_id = res["res_id"] + identity_wiz = ( + self.env["res.users.identitycheck"] + .sudo() + .browse(identity_wiz_id) + .with_user(self.test_user_1) + ) + identity_wiz.sudo().write({"password": "test_user_1"}) + + # Mock requests to avoid errors outside HTTP request + with ( + patch("odoo.addons.base.models.res_users.request", MagicMock()), + patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ), + ): + identity_wiz.sudo().run_check() + + self.assertTrue(review.status == "approved") + self.assertTrue(review.done_by == self.test_user_1) + self.assertTrue(review.comment == "Test Comment") + + def test_02_authentication_confirmation_without_comment(self): + # Set user password for validation + self.test_user_1.password = "test_user_1" + + # Create new test record + test_record = self.test_model.create({"test_field": 2.5}) + + # Create tier definition with no comment + require authentication + self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "has_comment": False, + "require_authentication": True, + } + ) + + # Request validation + review = test_record.with_user(self.test_user_2).request_validation() + self.assertTrue(review) + record = test_record.with_user(self.test_user_1) + + # Mock requests to avoid errors outside HTTP request + with patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ): + res = record.validate_tier() + + # Identity confirmation wizard + self.assertEqual(res["res_model"], "res.users.identitycheck") + identity_wiz_id = res["res_id"] + identity_wiz = ( + self.env["res.users.identitycheck"] + .sudo() + .browse(identity_wiz_id) + .with_user(self.test_user_1) + ) + identity_wiz.sudo().write({"password": "test_user_1"}) + + # Mock requests to avoid errors outside HTTP request + with ( + patch("odoo.addons.base.models.res_users.request", MagicMock()), + patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ), + ): + identity_wiz.sudo().run_check() + + self.assertTrue(review.status == "approved") + self.assertTrue(review.done_by == self.test_user_1) + + def test_03_authentication_confirmation_reject_with_comment(self): + # Set user password for validation + self.test_user_1.password = "test_user_1" + + # Create new test record + test_record = self.test_model.create({"test_field": 2.5}) + + # Create tier definitions + self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "has_comment": True, + "require_authentication": True, + } + ) + # Request validation + review = test_record.with_user(self.test_user_2.id).request_validation() + self.assertTrue(review) + + # Let _compute_can_review assign status 'pending' instead of waiting + review.flush_recordset() + record = test_record.with_user(self.test_user_1.id) + res = record.reject_tier() + ctx = res.get("context") + wizard = Form(self.env["comment.wizard"].with_context(**ctx)) + wizard.comment = "Test Comment" + wiz = wizard.save() + + # Mock requests to avoid errors outside HTTP request + with patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ): + res = wiz.add_comment() + + # Identity confirmation wizard + self.assertEqual(res["res_model"], "res.users.identitycheck") + identity_wiz_id = res["res_id"] + identity_wiz = ( + self.env["res.users.identitycheck"] + .sudo() + .browse(identity_wiz_id) + .with_user(self.test_user_1) + ) + identity_wiz.sudo().write({"password": "test_user_1"}) + + # Mock requests to avoid errors outside HTTP request + with ( + patch("odoo.addons.base.models.res_users.request", MagicMock()), + patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ), + ): + identity_wiz.sudo().run_check() + + self.assertTrue(review.status == "rejected") + self.assertTrue(review.done_by == self.test_user_1) + self.assertTrue(review.comment == "Test Comment") + + def test_04_authentication_confirmation_reject_without_comment(self): + # Set user password for validation + self.test_user_1.password = "test_user_1" + + # Create new test record + test_record = self.test_model.create({"test_field": 2.5}) + + # Create tier definition with no comment + require authentication + self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "has_comment": False, + "require_authentication": True, + } + ) + + # Request validation + review = test_record.with_user(self.test_user_2).request_validation() + self.assertTrue(review) + record = test_record.with_user(self.test_user_1) + + # Mock requests to avoid errors outside HTTP request + with patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ): + res = record.reject_tier() + + # Identity confirmation wizard + self.assertEqual(res["res_model"], "res.users.identitycheck") + identity_wiz_id = res["res_id"] + identity_wiz = ( + self.env["res.users.identitycheck"] + .sudo() + .browse(identity_wiz_id) + .with_user(self.test_user_1) + ) + identity_wiz.sudo().write({"password": "test_user_1"}) + + # Mock requests to avoid errors outside HTTP request + with ( + patch("odoo.addons.base.models.res_users.request", MagicMock()), + patch( + "odoo.addons.base_tier_validation_confirm_auth.models.res_users.request", + MagicMock(), + ), + ): + identity_wiz.sudo().run_check() + + self.assertTrue(review.status == "rejected") + self.assertTrue(review.done_by == self.test_user_1) diff --git a/base_tier_validation_confirm_auth/views/tier_definition_view.xml b/base_tier_validation_confirm_auth/views/tier_definition_view.xml new file mode 100644 index 0000000000..24df48b423 --- /dev/null +++ b/base_tier_validation_confirm_auth/views/tier_definition_view.xml @@ -0,0 +1,13 @@ + + + + tier.definition.form + tier.definition + + + + + + + + diff --git a/base_tier_validation_confirm_auth/wizards/__init__.py b/base_tier_validation_confirm_auth/wizards/__init__.py new file mode 100644 index 0000000000..2484d9e0ab --- /dev/null +++ b/base_tier_validation_confirm_auth/wizards/__init__.py @@ -0,0 +1 @@ +from . import comment_wizard diff --git a/base_tier_validation_confirm_auth/wizards/comment_wizard.py b/base_tier_validation_confirm_auth/wizards/comment_wizard.py new file mode 100644 index 0000000000..d1e9023343 --- /dev/null +++ b/base_tier_validation_confirm_auth/wizards/comment_wizard.py @@ -0,0 +1,19 @@ +# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + +from ..models.res_users import check_authentication + + +class CommentWizard(models.TransientModel): + _inherit = "comment.wizard" + + def add_comment(self): + if any(review.require_authentication for review in self.review_ids): + return self._add_comment_with_identity_check() + return super().add_comment() + + @check_authentication + def _add_comment_with_identity_check(self): + return super().add_comment() diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index 783a37c809..01b983f84a 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "odoo-addons-oca-server-ux" -version = "18.0.20260121.0" +version = "18.0.20260205.0" dependencies = [ "odoo-addon-announcement==18.0.*", "odoo-addon-barcode_action==18.0.*", @@ -15,6 +15,7 @@ dependencies = [ "odoo-addon-base_substate==18.0.*", "odoo-addon-base_technical_features==18.0.*", "odoo-addon-base_tier_validation==18.0.*", + "odoo-addon-base_tier_validation_confirm_auth==18.0.*", "odoo-addon-base_tier_validation_correction==18.0.*", "odoo-addon-base_tier_validation_formula==18.0.*", "odoo-addon-base_tier_validation_forward==18.0.*",