From cfa2b63fd73c4b7558600174000860349d7a95c5 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Sat, 2 May 2026 09:06:26 +0700 Subject: [PATCH] [ADD] base_tier_validation_multi --- base_tier_validation_multi/README.rst | 68 +++ base_tier_validation_multi/__init__.py | 4 + base_tier_validation_multi/__manifest__.py | 21 + base_tier_validation_multi/models/__init__.py | 5 + .../models/tier_definition.py | 94 ++++ .../models/tier_multi_validation_binding.py | 25 ++ .../models/tier_validation.py | 32 ++ base_tier_validation_multi/pyproject.toml | 3 + .../readme/CONTRIBUTORS.md | 1 + .../readme/DESCRIPTION.md | 1 + .../security/ir.model.access.csv | 3 + .../static/description/index.html | 418 ++++++++++++++++++ .../views/tier_definition_views.xml | 15 + base_tier_validation_multi/wizard/__init__.py | 3 + .../wizard/tier_validation_multi_wizard.py | 122 +++++ .../tier_validation_multi_wizard_view.xml | 47 ++ 16 files changed, 862 insertions(+) create mode 100644 base_tier_validation_multi/README.rst create mode 100644 base_tier_validation_multi/__init__.py create mode 100644 base_tier_validation_multi/__manifest__.py create mode 100644 base_tier_validation_multi/models/__init__.py create mode 100644 base_tier_validation_multi/models/tier_definition.py create mode 100644 base_tier_validation_multi/models/tier_multi_validation_binding.py create mode 100644 base_tier_validation_multi/models/tier_validation.py create mode 100644 base_tier_validation_multi/pyproject.toml create mode 100644 base_tier_validation_multi/readme/CONTRIBUTORS.md create mode 100644 base_tier_validation_multi/readme/DESCRIPTION.md create mode 100644 base_tier_validation_multi/security/ir.model.access.csv create mode 100644 base_tier_validation_multi/static/description/index.html create mode 100644 base_tier_validation_multi/views/tier_definition_views.xml create mode 100644 base_tier_validation_multi/wizard/__init__.py create mode 100644 base_tier_validation_multi/wizard/tier_validation_multi_wizard.py create mode 100644 base_tier_validation_multi/wizard/tier_validation_multi_wizard_view.xml diff --git a/base_tier_validation_multi/README.rst b/base_tier_validation_multi/README.rst new file mode 100644 index 00000000..2ca3344d --- /dev/null +++ b/base_tier_validation_multi/README.rst @@ -0,0 +1,68 @@ +========================== +Base Tier Validation Multi +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e3abddd269846c54665f60abd07b92c8f56d7f279ffbe6c0f068670781d68699 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-ecosoft--odoo%2Fecosoft--addons-lightgray.png?logo=github + :target: https://github.com/ecosoft-odoo/ecosoft-addons/tree/18.0/base_tier_validation_multi + :alt: ecosoft-odoo/ecosoft-addons + +|badge1| |badge2| |badge3| + +This module add Multi Validation for tier definition in Odoo + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Ecosoft + +Contributors +------------ + +- Saran Lim. + +Maintainers +----------- + +.. |maintainer-Saran440| image:: https://github.com/Saran440.png?size=40px + :target: https://github.com/Saran440 + :alt: Saran440 + +Current maintainer: + +|maintainer-Saran440| + +This module is part of the `ecosoft-odoo/ecosoft-addons `_ project on GitHub. + +You are welcome to contribute. diff --git a/base_tier_validation_multi/__init__.py b/base_tier_validation_multi/__init__.py new file mode 100644 index 00000000..207c81e0 --- /dev/null +++ b/base_tier_validation_multi/__init__.py @@ -0,0 +1,4 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import models +from . import wizard diff --git a/base_tier_validation_multi/__manifest__.py b/base_tier_validation_multi/__manifest__.py new file mode 100644 index 00000000..cf63ff0f --- /dev/null +++ b/base_tier_validation_multi/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2026 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Base Tier Validation Multi", + "summary": "Approve/Reject multiple records at once via tier validation", + "version": "18.0.1.0.0", + "category": "Tools", + "author": "Ecosoft", + "website": "https://github.com/ecosoft-odoo/ecosoft-addons", + "license": "AGPL-3", + "depends": [ + "base_tier_validation", + ], + "data": [ + "security/ir.model.access.csv", + "wizard/tier_validation_multi_wizard_view.xml", + "views/tier_definition_views.xml", + ], + "maintainers": ["Saran440"], +} diff --git a/base_tier_validation_multi/models/__init__.py b/base_tier_validation_multi/models/__init__.py new file mode 100644 index 00000000..ae9ec93b --- /dev/null +++ b/base_tier_validation_multi/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import tier_definition +from . import tier_multi_validation_binding +from . import tier_validation diff --git a/base_tier_validation_multi/models/tier_definition.py b/base_tier_validation_multi/models/tier_definition.py new file mode 100644 index 00000000..f2e26daf --- /dev/null +++ b/base_tier_validation_multi/models/tier_definition.py @@ -0,0 +1,94 @@ +# Copyright 2026 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class TierDefinition(models.Model): + _inherit = "tier.definition" + + allow_multi_validate = fields.Boolean( + string="Allow Multi Validation", + help="When enabled, automatically adds Validate/Reject Selected actions " + "to this model's list view.", + ) + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + models_to_sync = {v.get("model") for v in vals_list if v.get("model")} + for model_name in models_to_sync: + self._sync_multi_actions_for_model(model_name) + return records + + def write(self, vals): + old_models = set(self.mapped("model")) if "model" in vals else set() + res = super().write(vals) + if "allow_multi_validate" in vals or "model" in vals: + new_models = set(self.mapped("model")) + for model_name in old_models | new_models: + self._sync_multi_actions_for_model(model_name) + return res + + def unlink(self): + models_to_sync = set(self.mapped("model")) + res = super().unlink() + for model_name in models_to_sync: + self._sync_multi_actions_for_model(model_name) + return res + + @api.model + def _sync_multi_actions_for_model(self, model_name): + if not model_name: + return + has_multi = bool( + self.search( + [("model", "=", model_name), ("allow_multi_validate", "=", True)], + limit=1, + ) + ) + binding = self.env["tier.multi.validation.binding"].search( + [("model_name", "=", model_name)], limit=1 + ) + if has_multi and not binding: + self._create_multi_binding(model_name) + elif not has_multi and binding: + binding.unlink() + + @api.model + def _create_multi_binding(self, model_name): + ir_model = self.env["ir.model"].search([("model", "=", model_name)], limit=1) + if not ir_model: + return + + def _make_action(name, code): + return ( + self.env["ir.actions.server"] + .sudo() + .create( + { + "name": name, + "model_id": ir_model.id, + "binding_model_id": ir_model.id, + "binding_view_types": "list", + "state": "code", + "code": code, + } + ) + ) + + request_action = _make_action( + "Request Validation", + "action = records.action_multi_request_validation()", + ) + review_action = _make_action( + "Review Selected", + "action = records.action_multi_review()", + ) + self.env["tier.multi.validation.binding"].create( + { + "model_name": model_name, + "request_action_id": request_action.id, + "review_action_id": review_action.id, + } + ) diff --git a/base_tier_validation_multi/models/tier_multi_validation_binding.py b/base_tier_validation_multi/models/tier_multi_validation_binding.py new file mode 100644 index 00000000..787902eb --- /dev/null +++ b/base_tier_validation_multi/models/tier_multi_validation_binding.py @@ -0,0 +1,25 @@ +# Copyright 2026 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class TierMultiValidationBinding(models.Model): + _name = "tier.multi.validation.binding" + _description = "Tracks auto-created multi-validation server actions per model" + + model_name = fields.Char(required=True, index=True) + request_action_id = fields.Many2one( + comodel_name="ir.actions.server", + ondelete="set null", + ) + review_action_id = fields.Many2one( + comodel_name="ir.actions.server", + ondelete="set null", + ) + + def unlink(self): + actions = self.mapped("request_action_id") | self.mapped("review_action_id") + res = super().unlink() + actions.sudo().unlink() + return res diff --git a/base_tier_validation_multi/models/tier_validation.py b/base_tier_validation_multi/models/tier_validation.py new file mode 100644 index 00000000..7fe6c7bc --- /dev/null +++ b/base_tier_validation_multi/models/tier_validation.py @@ -0,0 +1,32 @@ +# Copyright 2026 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class TierValidation(models.AbstractModel): + _inherit = "tier.validation" + + def action_multi_request_validation(self): + return self._open_multi_tier_wizard("request") + + def action_multi_review(self): + return self._open_multi_tier_wizard("review") + + def _open_multi_tier_wizard(self, validate_reject): + names = { + "request": self.env._("Request Validation Multiple"), + "review": self.env._("Review Multiple"), + } + return { + "name": names[validate_reject], + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "tier.validation.multi.wizard", + "target": "new", + "context": { + "default_res_model": self._name, + "default_res_ids": str(self.ids), + "default_validate_reject": validate_reject, + }, + } diff --git a/base_tier_validation_multi/pyproject.toml b/base_tier_validation_multi/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/base_tier_validation_multi/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_tier_validation_multi/readme/CONTRIBUTORS.md b/base_tier_validation_multi/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..8d0d3315 --- /dev/null +++ b/base_tier_validation_multi/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Saran Lim. \<\> diff --git a/base_tier_validation_multi/readme/DESCRIPTION.md b/base_tier_validation_multi/readme/DESCRIPTION.md new file mode 100644 index 00000000..5e46f4b8 --- /dev/null +++ b/base_tier_validation_multi/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module add Multi Validation for tier definition in Odoo diff --git a/base_tier_validation_multi/security/ir.model.access.csv b/base_tier_validation_multi/security/ir.model.access.csv new file mode 100644 index 00000000..938d2cb5 --- /dev/null +++ b/base_tier_validation_multi/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_tier_validation_multi_wizard,access.tier.validation.multi.wizard,model_tier_validation_multi_wizard,base.group_user,1,1,1,1 +access_tier_multi_validation_binding,access.tier.multi.validation.binding,model_tier_multi_validation_binding,base.group_erp_manager,1,1,1,1 diff --git a/base_tier_validation_multi/static/description/index.html b/base_tier_validation_multi/static/description/index.html new file mode 100644 index 00000000..b68fddb9 --- /dev/null +++ b/base_tier_validation_multi/static/description/index.html @@ -0,0 +1,418 @@ + + + + + +Base Tier Validation Multi + + + +
+

Base Tier Validation Multi

+ + +

Beta License: AGPL-3 ecosoft-odoo/ecosoft-addons

+

This module add Multi Validation for tier definition in Odoo

+

Table of contents

+ +
+

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

+
    +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

Current maintainer:

+

Saran440

+

This module is part of the ecosoft-odoo/ecosoft-addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/base_tier_validation_multi/views/tier_definition_views.xml b/base_tier_validation_multi/views/tier_definition_views.xml new file mode 100644 index 00000000..5593c58c --- /dev/null +++ b/base_tier_validation_multi/views/tier_definition_views.xml @@ -0,0 +1,15 @@ + + + + tier.definition.form.multi + tier.definition + + + + + + + + + + diff --git a/base_tier_validation_multi/wizard/__init__.py b/base_tier_validation_multi/wizard/__init__.py new file mode 100644 index 00000000..a3e61c7f --- /dev/null +++ b/base_tier_validation_multi/wizard/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import tier_validation_multi_wizard diff --git a/base_tier_validation_multi/wizard/tier_validation_multi_wizard.py b/base_tier_validation_multi/wizard/tier_validation_multi_wizard.py new file mode 100644 index 00000000..955b125c --- /dev/null +++ b/base_tier_validation_multi/wizard/tier_validation_multi_wizard.py @@ -0,0 +1,122 @@ +# Copyright 2026 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import json + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class TierValidationMultiWizard(models.TransientModel): + _name = "tier.validation.multi.wizard" + _description = "Tier Validation Multi Wizard" + + res_model = fields.Char(required=True) + res_ids = fields.Char(help="JSON list of record IDs to validate/reject") + comment = fields.Char() + validate_reject = fields.Selection( + selection=[ + ("request", "Request Validation"), + ("review", "Review"), + ("validate", "Validate"), + ("reject", "Reject"), + ], + required=True, + default="review", + ) + record_count = fields.Integer(compute="_compute_record_count") + has_comment = fields.Boolean(compute="_compute_has_comment") + + @api.depends("res_ids", "res_model", "validate_reject") + def _compute_has_comment(self): + for wiz in self: + if wiz.validate_reject == "request" or not wiz.res_ids or not wiz.res_model: + wiz.has_comment = False + continue + ids = json.loads(wiz.res_ids or "[]") + records = ( + self.env[wiz.res_model].browse(ids).filtered(lambda r: r.can_review) + ) + found = False + for rec in records: + sequences = rec._get_sequences_to_approve(self.env.user) + if wiz.validate_reject == "validate": + reviews = rec.review_ids.filtered( + lambda x, sequences=sequences: ( + x.sequence in sequences or x.approve_sequence_bypass + ) + and x.definition_id.allow_multi_validate + ) + else: + reviews = rec.review_ids.filtered( + lambda x, sequences=sequences: x.sequence in sequences + and x.definition_id.allow_multi_validate + ) + if True in reviews.mapped("definition_id.has_comment"): + found = True + break + wiz.has_comment = found + + @api.depends("res_ids") + def _compute_record_count(self): + for wiz in self: + ids = json.loads(wiz.res_ids or "[]") + wiz.record_count = len(ids) + + def action_confirm(self): + """Entry point for Request Validation.""" + return self._do_confirm() + + def action_validate(self): + self.validate_reject = "validate" + return self._do_confirm() + + def action_reject(self): + self.validate_reject = "reject" + return self._do_confirm() + + def _do_confirm(self): + self.ensure_one() + ids = json.loads(self.res_ids or "[]") + if not ids: + raise UserError(self.env._("No records selected.")) + + if self.validate_reject == "request": + records = ( + self.env[self.res_model] + .browse(ids) + .filtered(lambda r: r.need_validation) + ) + if not records: + raise UserError( + self.env._("No selected records need a validation request.") + ) + records.request_validation() + return + + records = self.env[self.res_model].browse(ids).filtered(lambda r: r.can_review) + if not records: + raise UserError( + self.env._("None of the selected records can be reviewed by you.") + ) + for rec in records: + sequences = rec._get_sequences_to_approve(self.env.user) + if self.validate_reject == "validate": + reviews = rec.review_ids.filtered( + lambda x, sequences=sequences: ( + x.sequence in sequences or x.approve_sequence_bypass + ) + and x.definition_id.allow_multi_validate + ) + if self.comment: + reviews.write({"comment": self.comment}) + rec._validate_tier(reviews) + else: + reviews = rec.review_ids.filtered( + lambda x, sequences=sequences: x.sequence in sequences + and x.definition_id.allow_multi_validate + ) + if self.comment: + reviews.write({"comment": self.comment}) + rec._rejected_tier(reviews) + rec._update_counter({"review_deleted": True}) diff --git a/base_tier_validation_multi/wizard/tier_validation_multi_wizard_view.xml b/base_tier_validation_multi/wizard/tier_validation_multi_wizard_view.xml new file mode 100644 index 00000000..eecaec4d --- /dev/null +++ b/base_tier_validation_multi/wizard/tier_validation_multi_wizard_view.xml @@ -0,0 +1,47 @@ + + + + tier.validation.multi.wizard.form + tier.validation.multi.wizard + +
+ + + + + + + + + +
+
+
+