From 724017fb4f8b2c006ea761fbee2d48279b75d9b7 Mon Sep 17 00:00:00 2001 From: Wesley Oliveira Date: Wed, 4 Feb 2026 18:52:23 -0300 Subject: [PATCH] [ADD] tier_validation_dynamic_state: add new module --- test-requirements.txt | 1 + tier_validation_dynamic_state/README.rst | 84 ++++ tier_validation_dynamic_state/__init__.py | 1 + tier_validation_dynamic_state/__manifest__.py | 18 + .../models/__init__.py | 3 + .../models/tier_definition.py | 17 + .../models/tier_review.py | 11 + .../models/tier_validation.py | 188 ++++++++ tier_validation_dynamic_state/pyproject.toml | 3 + .../readme/CONTRIBUTORS.md | 3 + .../readme/DESCRIPTION.md | 1 + tier_validation_dynamic_state/readme/USAGE.md | 6 + .../static/description/icon.png | Bin 0 -> 2873 bytes .../static/description/index.html | 436 ++++++++++++++++++ .../templates/tier_validation_templates.xml | 16 + .../tests/__init__.py | 1 + .../test_tier_validation_dynamic_state.py | 125 +++++ .../views/tier_definition_view.xml | 40 ++ 18 files changed, 954 insertions(+) create mode 100644 test-requirements.txt create mode 100644 tier_validation_dynamic_state/README.rst create mode 100644 tier_validation_dynamic_state/__init__.py create mode 100644 tier_validation_dynamic_state/__manifest__.py create mode 100644 tier_validation_dynamic_state/models/__init__.py create mode 100644 tier_validation_dynamic_state/models/tier_definition.py create mode 100644 tier_validation_dynamic_state/models/tier_review.py create mode 100644 tier_validation_dynamic_state/models/tier_validation.py create mode 100644 tier_validation_dynamic_state/pyproject.toml create mode 100644 tier_validation_dynamic_state/readme/CONTRIBUTORS.md create mode 100644 tier_validation_dynamic_state/readme/DESCRIPTION.md create mode 100644 tier_validation_dynamic_state/readme/USAGE.md create mode 100644 tier_validation_dynamic_state/static/description/icon.png create mode 100644 tier_validation_dynamic_state/static/description/index.html create mode 100644 tier_validation_dynamic_state/templates/tier_validation_templates.xml create mode 100644 tier_validation_dynamic_state/tests/__init__.py create mode 100644 tier_validation_dynamic_state/tests/test_tier_validation_dynamic_state.py create mode 100644 tier_validation_dynamic_state/views/tier_definition_view.xml diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..66bc2cb --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +odoo_test_helper diff --git a/tier_validation_dynamic_state/README.rst b/tier_validation_dynamic_state/README.rst new file mode 100644 index 0000000..c6e35b0 --- /dev/null +++ b/tier_validation_dynamic_state/README.rst @@ -0,0 +1,84 @@ +============================= +Tier Validation Dynamic State +============================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:bcafd8e79751d2060b7bfa49c3b3c19b07407706bf2d6599b39e58ffaf059810 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-Escodoo%2Fserver--addons-lightgray.png?logo=github + :target: https://github.com/Escodoo/server-addons/tree/18.0/tier_validation_dynamic_state + :alt: Escodoo/server-addons + +|badge1| |badge2| |badge3| + +This module extends the base tier validation to configure state from and +state to in the tier definition. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +- Open Settings > Tier Definition and create a new definition. +- Select the model, the From State, and the To State for the + validation. +- When a user tries to change the state of a record, if there is a + definition for the current state and the target state, a validation + request will be created automatically. +- The Request Validation button will be hidden. + +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 +------- + +* Escodoo + +Contributors +------------ + +- ``Escodoo ``\ \_: + + - Wesley Oliveira wesley.oliveira@escodoo.com.br + +Maintainers +----------- + +.. |maintainer-WesleyOliveira98| image:: https://github.com/WesleyOliveira98.png?size=40px + :target: https://github.com/WesleyOliveira98 + :alt: WesleyOliveira98 + +Current maintainer: + +|maintainer-WesleyOliveira98| + +This module is part of the `Escodoo/server-addons `_ project on GitHub. + +You are welcome to contribute. diff --git a/tier_validation_dynamic_state/__init__.py b/tier_validation_dynamic_state/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/tier_validation_dynamic_state/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/tier_validation_dynamic_state/__manifest__.py b/tier_validation_dynamic_state/__manifest__.py new file mode 100644 index 0000000..d0a71c7 --- /dev/null +++ b/tier_validation_dynamic_state/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2026 - TODAY, Escodoo +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Tier Validation Dynamic State", + "summary": "Implement a validation process based on states.", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Escodoo", + "website": "https://github.com/Escodoo/server-addons", + "depends": ["base_tier_validation"], + "data": [ + "views/tier_definition_view.xml", + "templates/tier_validation_templates.xml", + ], + "installable": True, + "maintainers": ["WesleyOliveira98"], +} diff --git a/tier_validation_dynamic_state/models/__init__.py b/tier_validation_dynamic_state/models/__init__.py new file mode 100644 index 0000000..c649fbf --- /dev/null +++ b/tier_validation_dynamic_state/models/__init__.py @@ -0,0 +1,3 @@ +from . import tier_definition +from . import tier_review +from . import tier_validation diff --git a/tier_validation_dynamic_state/models/tier_definition.py b/tier_validation_dynamic_state/models/tier_definition.py new file mode 100644 index 0000000..6311994 --- /dev/null +++ b/tier_validation_dynamic_state/models/tier_definition.py @@ -0,0 +1,17 @@ +# Copyright 2026 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class TierDefinition(models.Model): + _inherit = "tier.definition" + + state_from = fields.Many2one( + comodel_name="ir.model.fields.selection", + ondelete="cascade", + ) + state_to = fields.Many2one( + comodel_name="ir.model.fields.selection", + ondelete="cascade", + ) diff --git a/tier_validation_dynamic_state/models/tier_review.py b/tier_validation_dynamic_state/models/tier_review.py new file mode 100644 index 0000000..590d170 --- /dev/null +++ b/tier_validation_dynamic_state/models/tier_review.py @@ -0,0 +1,11 @@ +# Copyright 2026 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class TierReview(models.Model): + _inherit = "tier.review" + + state_from = fields.Char(readonly=True) + state_to = fields.Char(readonly=True) diff --git a/tier_validation_dynamic_state/models/tier_validation.py b/tier_validation_dynamic_state/models/tier_validation.py new file mode 100644 index 0000000..96aad57 --- /dev/null +++ b/tier_validation_dynamic_state/models/tier_validation.py @@ -0,0 +1,188 @@ +# Copyright 2026 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from lxml import etree + +from odoo import api, models + + +class TierValidation(models.AbstractModel): + _inherit = "tier.validation" + + @api.depends("review_ids", "review_ids.status", "review_ids.state_from") + def _compute_validation_status(self): + for item in self: + current_state = item._tier_validation_get_current_state_value() + reviews = item.review_ids.filtered( + lambda r, cs=current_state: r.state_from == cs + ) + + if not reviews: + item.validation_status = "no" + continue + + validated_states = item._validated_states() + rejected_states = item._rejected_states() + + if any(reviews.filtered(lambda x, rs=rejected_states: x.status in rs)): + item.validation_status = "rejected" + elif all(x.status in validated_states for x in reviews): + item.validation_status = "validated" + elif any(reviews.filtered(lambda x: x.status == "pending")): + item.validation_status = "pending" + else: + item.validation_status = "waiting" + + def _compute_need_validation(self): + for rec in self: + if not rec.id: + rec.need_validation = False + continue + + current_state = rec._tier_validation_get_current_state_value() + tier_definitions = ( + self.env["tier.definition"] + .sudo() + .search( + [ + ("model", "=", self._name), + ("company_id", "in", [False] + rec._get_company().ids), + ("state_from.value", "=", current_state), + ] + ) + ) + + valid_tiers = any([rec.evaluate_tier(tier) for tier in tier_definitions]) + current_reviews = rec.review_ids.filtered( + lambda r, cs=current_state: r.state_from == cs + ) + + rec.need_validation = not current_reviews and valid_tiers + + def _prepare_tier_review_vals(self, definition, sequence): + vals = super()._prepare_tier_review_vals(definition, sequence) + vals.update( + { + "state_from": definition.state_from.value, + "state_to": definition.state_to.value, + } + ) + return vals + + def _check_state_conditions(self, vals): + self.ensure_one() + state_field = self._state_field + if state_field not in vals: + return False + + current_state = self._tier_validation_get_current_state_value() + target_state = vals[state_field] + + matching_tiers = ( + self.env["tier.definition"] + .sudo() + .search( + [ + ("model", "=", self._name), + ("company_id", "in", [False] + self._get_company().ids), + ("state_from.value", "=", current_state), + ("state_to.value", "=", target_state), + ] + ) + ) + return any(self.evaluate_tier(tier) for tier in matching_tiers) + + def request_validation(self, target_state=None): + if not target_state: + return super().request_validation() + + td_obj = self.env["tier.definition"] + tr_obj = self.env["tier.review"] + vals_list = [] + for rec in self: + current_state = rec._tier_validation_get_current_state_value() + domain = [ + ("model", "=", self._name), + ("company_id", "in", [False] + rec._get_company().ids), + ("state_from.value", "=", current_state), + ] + if target_state: + domain.append(("state_to.value", "=", target_state)) + + tier_definitions = td_obj.sudo().search(domain, order="sequence desc") + + sequence = len(rec.review_ids) + for td in tier_definitions: + if rec.evaluate_tier(td): + sequence += 1 + vals_list.append(rec._prepare_tier_review_vals(td, sequence)) + + created_trs = tr_obj.create(vals_list) + self._notify_review_requested(created_trs) + return created_trs + + def _check_and_request_tier(self, target_state): + self.ensure_one() + tier_definitions = ( + self.env["tier.definition"] + .sudo() + .search( + [ + ("model", "=", self._name), + ("state_from.value", "=", self.state), + ("state_to.value", "=", target_state), + ], + ) + ) + + if not tier_definitions: + return False + + current_reviews = self.review_ids.filtered( + lambda r: r.state_from == self.state and r.state_to == target_state + ) + + if not current_reviews: + self.request_validation(target_state=target_state) + return True + + if any(review.status != "approved" for review in current_reviews): + return True + + return False + + def write(self, vals): + if self._state_field in vals: + target_state = vals[self._state_field] + needs_validation = False + for rec in self: + if rec._check_and_request_tier(target_state): + needs_validation = True + + if needs_validation: + return True + return super().write(vals) + + def _add_tier_validation_buttons(self, node, params): + str_element = self.env["ir.qweb"]._render( + "tier_validation_dynamic_state.tier_validation_buttons_hide_request", params + ) + new_node = etree.fromstring(str_element) + return new_node + + def _compute_hide_reviews(self): + for rec in self: + hide_reviews = True + tier_definitions = ( + rec.env["tier.definition"] + .sudo() + .search( + [ + ("model", "=", rec._name), + ("state_from.value", "=", rec.state), + ], + ) + ) + if tier_definitions: + hide_reviews = False + rec.hide_reviews = hide_reviews diff --git a/tier_validation_dynamic_state/pyproject.toml b/tier_validation_dynamic_state/pyproject.toml new file mode 100644 index 0000000..4231d0c --- /dev/null +++ b/tier_validation_dynamic_state/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/tier_validation_dynamic_state/readme/CONTRIBUTORS.md b/tier_validation_dynamic_state/readme/CONTRIBUTORS.md new file mode 100644 index 0000000..2f9bd6d --- /dev/null +++ b/tier_validation_dynamic_state/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +* `Escodoo `_: + + * Wesley Oliveira diff --git a/tier_validation_dynamic_state/readme/DESCRIPTION.md b/tier_validation_dynamic_state/readme/DESCRIPTION.md new file mode 100644 index 0000000..86ced48 --- /dev/null +++ b/tier_validation_dynamic_state/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module extends the base tier validation to configure state from and state to in the tier definition. diff --git a/tier_validation_dynamic_state/readme/USAGE.md b/tier_validation_dynamic_state/readme/USAGE.md new file mode 100644 index 0000000..e128142 --- /dev/null +++ b/tier_validation_dynamic_state/readme/USAGE.md @@ -0,0 +1,6 @@ +To use this module, you need to: + +* Open Settings > Tier Definition and create a new definition. +* Select the model, the From State, and the To State for the validation. +* When a user tries to change the state of a record, if there is a definition for the current state and the target state, a validation request will be created automatically. +* The Request Validation button will be hidden. diff --git a/tier_validation_dynamic_state/static/description/icon.png b/tier_validation_dynamic_state/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..12ab0051e3b9ac413f0f0016b7cff54f02fc763e GIT binary patch literal 2873 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 XXqTGI$C0NT00000NkvXXu0mjf?G|(} literal 0 HcmV?d00001 diff --git a/tier_validation_dynamic_state/static/description/index.html b/tier_validation_dynamic_state/static/description/index.html new file mode 100644 index 0000000..1c029f5 --- /dev/null +++ b/tier_validation_dynamic_state/static/description/index.html @@ -0,0 +1,436 @@ + + + + + +Tier Validation Dynamic State + + + +
+

Tier Validation Dynamic State

+ + +

Beta License: AGPL-3 Escodoo/server-addons

+

This module extends the base tier validation to configure state from and +state to in the tier definition.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  • Open Settings > Tier Definition and create a new definition.
  • +
  • Select the model, the From State, and the To State for the +validation.
  • +
  • When a user tries to change the state of a record, if there is a +definition for the current state and the target state, a validation +request will be created automatically.
  • +
  • The Request Validation button will be hidden.
  • +
+
+
+

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

+
    +
  • Escodoo
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

Current maintainer:

+

WesleyOliveira98

+

This module is part of the Escodoo/server-addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/tier_validation_dynamic_state/templates/tier_validation_templates.xml b/tier_validation_dynamic_state/templates/tier_validation_templates.xml new file mode 100644 index 0000000..3330e38 --- /dev/null +++ b/tier_validation_dynamic_state/templates/tier_validation_templates.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/tier_validation_dynamic_state/tests/__init__.py b/tier_validation_dynamic_state/tests/__init__.py new file mode 100644 index 0000000..6290bf8 --- /dev/null +++ b/tier_validation_dynamic_state/tests/__init__.py @@ -0,0 +1 @@ +from . import test_tier_validation_dynamic_state diff --git a/tier_validation_dynamic_state/tests/test_tier_validation_dynamic_state.py b/tier_validation_dynamic_state/tests/test_tier_validation_dynamic_state.py new file mode 100644 index 0000000..7ef2793 --- /dev/null +++ b/tier_validation_dynamic_state/tests/test_tier_validation_dynamic_state.py @@ -0,0 +1,125 @@ +# Copyright 2026 - TODAY, Wesley Oliveira +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import tagged + +from odoo.addons.base_tier_validation.tests.common import CommonTierValidation + + +@tagged("post_install", "-at_install") +class TestTierValidationDynamicState(CommonTierValidation): + def setUp(self): + super().setUp() + + def get_selection_id(model_name, value): + return ( + self.env["ir.model.fields.selection"] + .search( + [ + ("field_id.model_id", "=", model_name), + ("field_id.name", "=", "state"), + ("value", "=", value), + ], + limit=1, + ) + .id + ) + + self.draft_state = get_selection_id("tier.validation.tester", "draft") + self.confirmed_state = get_selection_id("tier.validation.tester", "confirmed") + self.cancel_state = get_selection_id("tier.validation.tester", "cancel") + + self.tier_def_1 = self.tier_def_obj.create( + { + "name": "Tier 1", + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "state_from": self.draft_state, + "state_to": self.confirmed_state, + } + ) + self.tier_def_2 = self.tier_def_obj.create( + { + "name": "Tier 2", + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_2.id, + "state_from": self.confirmed_state, + "state_to": self.cancel_state, + } + ) + + def test_compute_need_validation(self): + record = self.test_record + self.assertTrue(record.need_validation) + + record.request_validation(target_state="confirmed") + + record.flush_recordset() + record.invalidate_recordset() + self.env.registry.clear_cache() + self.assertFalse(record.need_validation) + + def test_create_review_on_write(self): + record = self.test_record + self.assertEqual(record.state, "draft") + self.assertFalse(record.review_ids) + + res = record.write({"state": "confirmed"}) + + self.assertTrue(res) + self.assertEqual(record.state, "draft") + self.assertTrue(record.review_ids) + + review = record.review_ids[0] + self.assertEqual(review.state_from, "draft") + self.assertEqual(review.state_to, "confirmed") + + def test_compute_validation_status(self): + record = self.test_record + + record.request_validation(target_state="confirmed") + record.review_ids.write({"status": "approved"}) + self.assertEqual(record.validation_status, "validated") + + record.write({"state": "confirmed"}) + + record._compute_validation_status() + self.assertEqual(record.validation_status, "no") + + record.request_validation(target_state="cancel") + record.review_ids.filtered(lambda r: r.state_from == "confirmed").write( + {"status": "rejected"} + ) + record._compute_validation_status() + self.assertEqual(record.validation_status, "rejected") + + def test_request_validation_with_target_state(self): + record = self.test_record + + self.tier_def_obj.create( + { + "name": "Draft to Cancel", + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "state_from": self.draft_state, + "state_to": self.cancel_state, + } + ) + + record.request_validation(target_state="cancel") + self.assertEqual(len(record.review_ids), 1) + self.assertEqual(record.review_ids.state_to, "cancel") + + def test_check_state_conditions(self): + record = self.test_record + + self.assertTrue(record._check_state_conditions({"state": "confirmed"})) + self.assertFalse(record._check_state_conditions({"state": "unknown"})) + + def test_prepare_tier_review_vals(self): + vals = self.test_record._prepare_tier_review_vals(self.tier_def_1, 1) + self.assertEqual(vals["state_from"], "draft") + self.assertEqual(vals["state_to"], "confirmed") diff --git a/tier_validation_dynamic_state/views/tier_definition_view.xml b/tier_validation_dynamic_state/views/tier_definition_view.xml new file mode 100644 index 0000000..004b283 --- /dev/null +++ b/tier_validation_dynamic_state/views/tier_definition_view.xml @@ -0,0 +1,40 @@ + + + + + tier.definition.form.inherit + tier.definition + + + + + + + + + + + tier.definition.list.inherit + tier.definition + + + + + + + + +