diff --git a/base_tier_validation_review_history/README.rst b/base_tier_validation_review_history/README.rst new file mode 100644 index 00000000..da05fe53 --- /dev/null +++ b/base_tier_validation_review_history/README.rst @@ -0,0 +1,125 @@ +=================================== +Base Tier Validation Review History +=================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ffe053f3b8b2e6a43c232e5919a7c99135700ea151d5d8fd720cda8e1cabae4a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |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/base_tier_validation_review_history + :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-base_tier_validation_review_history + :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| + +This module extends *Base Tier Validation* to keep completed tier reviews as +history instead of deleting them once a validation cycle ends (e.g. on +rejection restart or when the record is reset/cancelled). + +Without this module, reviews are removed when the validation cycle is reset, so +there is no trace of who approved or rejected a document in a previous cycle. +With this module, completed reviews (approved/rejected) can be archived and kept +as a *Reviews History*, viewable on the document and from a dedicated menu. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Keeping tier review history is resolved from two configuration levels: + +#. **Company** → *Settings > Tier Validation > Keep Tier Review History* + (boolean). Sets the default for all tier definitions of the company. In + multi-company, switch to the relevant company before changing it. +#. **Tier Definition** → *Keep Tier Review History* (selection: *Keep* / *Do + Not Keep* / empty), on the *More Options* tab of the tier definition + (*Settings > Technical > Tier Validations > Tier Definitions*). Leave empty + to inherit the company default. *Keep* or *Do Not Keep* overrides the company + default for this tier definition. + +When keeping is effective for a tier definition, its completed +(approved/rejected) reviews are archived as history instead of being deleted +when the validation cycle ends. The archived reviews are shown in the *Reviews +History* section of the document and can be browsed from +*Settings > Technical > Tier Validations > Tier Reviews*. + +The *Reviews History* section is injected automatically only into documents +whose tier validation view is built automatically +(``_tier_validation_manual_config = False``), which is the case of the usual +consumers (purchase, sale, account move). A model that configures its tier +validation view manually must add ```` to +its form view to display the history. + +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 +~~~~~~~ + +* Quartile + +Contributors +~~~~~~~~~~~~ + +* `Quartile `__: + + * Shinnosuke Morita + +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. + +.. |maintainer-smorita7749| image:: https://github.com/smorita7749.png?size=40px + :target: https://github.com/smorita7749 + :alt: smorita7749 + +Current `maintainer `__: + +|maintainer-smorita7749| + +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_review_history/__init__.py b/base_tier_validation_review_history/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/base_tier_validation_review_history/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/base_tier_validation_review_history/__manifest__.py b/base_tier_validation_review_history/__manifest__.py new file mode 100644 index 00000000..297794f0 --- /dev/null +++ b/base_tier_validation_review_history/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Base Tier Validation Review History", + "summary": "Keep completed tier reviews as history instead of deleting them", + "version": "16.0.1.0.0", + "category": "Tools", + "website": "https://github.com/OCA/server-ux", + "author": "Quartile, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["base_tier_validation"], + "data": [ + "views/res_config_settings_views.xml", + "views/tier_definition_view.xml", + "views/tier_review_view.xml", + "templates/tier_validation_templates.xml", + ], + "development_status": "Alpha", + "maintainers": ["smorita7749"], + "installable": True, +} diff --git a/base_tier_validation_review_history/models/__init__.py b/base_tier_validation_review_history/models/__init__.py new file mode 100644 index 00000000..bb0e8dd5 --- /dev/null +++ b/base_tier_validation_review_history/models/__init__.py @@ -0,0 +1,5 @@ +from . import res_company +from . import res_config_settings +from . import tier_definition +from . import tier_review +from . import tier_validation diff --git a/base_tier_validation_review_history/models/res_company.py b/base_tier_validation_review_history/models/res_company.py new file mode 100644 index 00000000..fe417e12 --- /dev/null +++ b/base_tier_validation_review_history/models/res_company.py @@ -0,0 +1,12 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + tier_validation_keep_review_history = fields.Boolean( + string="Keep Tier Review History" + ) diff --git a/base_tier_validation_review_history/models/res_config_settings.py b/base_tier_validation_review_history/models/res_config_settings.py new file mode 100644 index 00000000..0e7d4454 --- /dev/null +++ b/base_tier_validation_review_history/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + tier_validation_keep_review_history = fields.Boolean( + related="company_id.tier_validation_keep_review_history", + readonly=False, + ) diff --git a/base_tier_validation_review_history/models/tier_definition.py b/base_tier_validation_review_history/models/tier_definition.py new file mode 100644 index 00000000..b6cd1b95 --- /dev/null +++ b/base_tier_validation_review_history/models/tier_definition.py @@ -0,0 +1,32 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class TierDefinition(models.Model): + _inherit = "tier.definition" + + keep_review_history = fields.Selection( + selection=[ + ("keep", "Keep"), + ("no_keep", "Do Not Keep"), + ], + string="Keep Tier Review History", + help="Override the company default for keeping completed reviews as " + "history. Leave empty to inherit the company setting.", + ) + + def _keep_review_history_enabled(self): + """Resolve whether completed reviews of this definition are kept as + history. The definition overrides the company default in either + direction; an empty value inherits the company setting.""" + self.ensure_one() + if self.keep_review_history == "keep": + return True + if self.keep_review_history == "no_keep": + return False + # A definition without company applies to every company, so it falls back + # to the setting of the company the validation is running for. + company = self.company_id or self.env.company + return company.tier_validation_keep_review_history diff --git a/base_tier_validation_review_history/models/tier_review.py b/base_tier_validation_review_history/models/tier_review.py new file mode 100644 index 00000000..847df243 --- /dev/null +++ b/base_tier_validation_review_history/models/tier_review.py @@ -0,0 +1,30 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class TierReview(models.Model): + _inherit = "tier.review" + + active = fields.Boolean(default=True) + record_reference = fields.Reference( + string="Document", + selection="_selection_record_reference", + compute="_compute_record_reference", + help="The document under validation, as a clickable link.", + ) + + @api.model + def _selection_record_reference(self): + model_names = self.env["tier.definition"]._get_tier_validation_model_names() + ir_models = self.env["ir.model"].sudo().search([("model", "in", model_names)]) + return [(m.model, m.name) for m in ir_models] + + @api.depends("model", "res_id") + def _compute_record_reference(self): + for record in self: + if record.model and record.res_id and record.model in self.env: + record.record_reference = "{},{}".format(record.model, record.res_id) + else: + record.record_reference = None diff --git a/base_tier_validation_review_history/models/tier_validation.py b/base_tier_validation_review_history/models/tier_validation.py new file mode 100644 index 00000000..616fcec8 --- /dev/null +++ b/base_tier_validation_review_history/models/tier_validation.py @@ -0,0 +1,65 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from lxml import etree + +from odoo import fields, models + + +class TierValidation(models.AbstractModel): + _inherit = "tier.validation" + + review_history_ids = fields.One2many( + comodel_name="tier.review", + inverse_name="res_id", + string="Review History", + domain=lambda self: [("model", "=", self._name), ("active", "=", False)], + context={"active_test": False}, + ) + + def _remove_reviews(self): + """Archive completed reviews configured to keep history; delete the rest.""" + to_archive = self.review_ids.filtered( + lambda r: r.status in ("approved", "rejected") + and r.definition_id + and r.definition_id._keep_review_history_enabled() + ) + if to_archive: + to_archive.write({"active": False}) + # Archiving changes no FK/status, so nothing invalidates the caches of + # review_ids / review_history_ids or of the fields derived from them. + # need_validation, has_comment, next_review and is_reevaluation_required + # are computed without @api.depends in base, so modified() cannot flag + # them either and they have to be invalidated explicitly. + self.invalidate_recordset( + [ + "review_ids", + "review_history_ids", + "need_validation", + "has_comment", + "next_review", + "is_reevaluation_required", + ] + ) + self.modified(["review_ids"]) + return super( + TierValidation, self.with_context(active_test=True) + )._remove_reviews() + + def unlink(self): + # active_test=False so archived history reviews are deleted too, not orphaned. + self.with_context(active_test=False).mapped("review_ids").unlink() + return super().unlink() + + def _add_tier_validation_reviews(self, node, params): + """Render the base reviews block and append the review-history block, + wrapped in a single root element (the caller expects one node).""" + review_node = super()._add_tier_validation_reviews(node, params) + history_str = self.env["ir.qweb"]._render( + "base_tier_validation_review_history.tier_validation_review_history", + params, + ) + wrapper = etree.Element("div") + wrapper.append(review_node) + wrapper.append(etree.fromstring(history_str)) + return wrapper diff --git a/base_tier_validation_review_history/readme/CONFIGURE.rst b/base_tier_validation_review_history/readme/CONFIGURE.rst new file mode 100644 index 00000000..70db2170 --- /dev/null +++ b/base_tier_validation_review_history/readme/CONFIGURE.rst @@ -0,0 +1,23 @@ +Keeping tier review history is resolved from two configuration levels: + +#. **Company** → *Settings > Tier Validation > Keep Tier Review History* + (boolean). Sets the default for all tier definitions of the company. In + multi-company, switch to the relevant company before changing it. +#. **Tier Definition** → *Keep Tier Review History* (selection: *Keep* / *Do + Not Keep* / empty), on the *More Options* tab of the tier definition + (*Settings > Technical > Tier Validations > Tier Definitions*). Leave empty + to inherit the company default. *Keep* or *Do Not Keep* overrides the company + default for this tier definition. + +When keeping is effective for a tier definition, its completed +(approved/rejected) reviews are archived as history instead of being deleted +when the validation cycle ends. The archived reviews are shown in the *Reviews +History* section of the document and can be browsed from +*Settings > Technical > Tier Validations > Tier Reviews*. + +The *Reviews History* section is injected automatically only into documents +whose tier validation view is built automatically +(``_tier_validation_manual_config = False``), which is the case of the usual +consumers (purchase, sale, account move). A model that configures its tier +validation view manually must add ```` to +its form view to display the history. diff --git a/base_tier_validation_review_history/readme/CONTRIBUTORS.rst b/base_tier_validation_review_history/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..64c5b77b --- /dev/null +++ b/base_tier_validation_review_history/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Quartile `__: + + * Shinnosuke Morita diff --git a/base_tier_validation_review_history/readme/DESCRIPTION.rst b/base_tier_validation_review_history/readme/DESCRIPTION.rst new file mode 100644 index 00000000..b169e42c --- /dev/null +++ b/base_tier_validation_review_history/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module extends *Base Tier Validation* to keep completed tier reviews as +history instead of deleting them once a validation cycle ends (e.g. on +rejection restart or when the record is reset/cancelled). + +Without this module, reviews are removed when the validation cycle is reset, so +there is no trace of who approved or rejected a document in a previous cycle. +With this module, completed reviews (approved/rejected) can be archived and kept +as a *Reviews History*, viewable on the document and from a dedicated menu. diff --git a/base_tier_validation_review_history/static/description/index.html b/base_tier_validation_review_history/static/description/index.html new file mode 100644 index 00000000..b878ee86 --- /dev/null +++ b/base_tier_validation_review_history/static/description/index.html @@ -0,0 +1,466 @@ + + + + + +Base Tier Validation Review History + + + +
+

Base Tier Validation Review History

+ + +

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

+

This module extends Base Tier Validation to keep completed tier reviews as +history instead of deleting them once a validation cycle ends (e.g. on +rejection restart or when the record is reset/cancelled).

+

Without this module, reviews are removed when the validation cycle is reset, so +there is no trace of who approved or rejected a document in a previous cycle. +With this module, completed reviews (approved/rejected) can be archived and kept +as a Reviews History, viewable on the document and from a dedicated menu.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

Keeping tier review history is resolved from two configuration levels:

+
    +
  1. CompanySettings > Tier Validation > Keep Tier Review History +(boolean). Sets the default for all tier definitions of the company. In +multi-company, switch to the relevant company before changing it.
  2. +
  3. Tier DefinitionKeep Tier Review History (selection: Keep / Do +Not Keep / empty), on the More Options tab of the tier definition +(Settings > Technical > Tier Validations > Tier Definitions). Leave empty +to inherit the company default. Keep or Do Not Keep overrides the company +default for this tier definition.
  4. +
+

When keeping is effective for a tier definition, its completed +(approved/rejected) reviews are archived as history instead of being deleted +when the validation cycle ends. The archived reviews are shown in the Reviews +History section of the document and can be browsed from +Settings > Technical > Tier Validations > Tier Reviews.

+

The Reviews History section is injected automatically only into documents +whose tier validation view is built automatically +(_tier_validation_manual_config = False), which is the case of the usual +consumers (purchase, sale, account move). A model that configures its tier +validation view manually must add <field name="review_history_ids" /> to +its form view to display the history.

+
+
+

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

+
    +
  • Quartile
  • +
+
+
+

Contributors

+ +
+
+

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.

+

Current maintainer:

+

smorita7749

+

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_review_history/templates/tier_validation_templates.xml b/base_tier_validation_review_history/templates/tier_validation_templates.xml new file mode 100644 index 00000000..4a700136 --- /dev/null +++ b/base_tier_validation_review_history/templates/tier_validation_templates.xml @@ -0,0 +1,26 @@ + + + + diff --git a/base_tier_validation_review_history/tests/__init__.py b/base_tier_validation_review_history/tests/__init__.py new file mode 100644 index 00000000..cac2cdae --- /dev/null +++ b/base_tier_validation_review_history/tests/__init__.py @@ -0,0 +1 @@ +from . import test_tier_validation_review_history diff --git a/base_tier_validation_review_history/tests/test_tier_validation_review_history.py b/base_tier_validation_review_history/tests/test_tier_validation_review_history.py new file mode 100644 index 00000000..1082118f --- /dev/null +++ b/base_tier_validation_review_history/tests/test_tier_validation_review_history.py @@ -0,0 +1,200 @@ +# Copyright 2026 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from lxml import etree + +from odoo.tests.common import Form + +from odoo.addons.base_tier_validation.tests.common import CommonTierValidation + + +class TestTierValidationReviewHistory(CommonTierValidation): + def _reject_and_restart(self, record): + record.reject_tier() + review = record.review_ids + record.restart_validation() + return review + + # --- Resolution of the effective "keep history" setting ----------------- + + def test_definition_keep_archives(self): + """Definition set to 'keep' -> completed review archived on restart.""" + self.tier_definition.keep_review_history = "keep" + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + review = self._reject_and_restart(record) + self.assertFalse(review.active) + self.assertEqual(review.status, "rejected") + self.assertFalse(record.review_ids) + self.assertEqual(record.review_history_ids, review) + # Archiving must refresh review_ids-dependent computes (else the buttons + # and reviews panel would not display, as validation_status stays "rejected"). + self.assertEqual(record.validation_status, "no") + self.assertTrue(record.need_validation) + # A fresh cycle keeps the archived review separate from the new one. + self.test_record.with_user(self.test_user_2.id).request_validation() + self.assertEqual(record.review_history_ids, review) + + def test_definition_no_keep_overrides_company(self): + """Definition 'no_keep' overrides an enabled company default -> deleted.""" + self.tier_definition.company_id = self.env.company + self.env.company.tier_validation_keep_review_history = True + self.tier_definition.keep_review_history = "no_keep" + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + self._reject_and_restart(record) + self.assertFalse(record.review_ids) + self.assertFalse(record.review_history_ids) + + def test_company_default_keep_inherited(self): + """Company default 'keep' + empty definition -> archived (inherit).""" + self.tier_definition.company_id = self.env.company + self.env.company.tier_validation_keep_review_history = True + # definition keep_review_history left empty + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + review = self._reject_and_restart(record) + self.assertFalse(review.active) + self.assertEqual(record.review_history_ids, review) + + def test_global_definition_inherits_company_default(self): + """Definition without company (cross-company) -> inherits env.company.""" + self.tier_definition.company_id = False + self.env.company.tier_validation_keep_review_history = True + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + review = self._reject_and_restart(record) + self.assertFalse(review.active) + self.assertEqual(record.review_history_ids, review) + + def test_company_default_off_deletes(self): + """Company default off + empty definition -> deleted (base behavior).""" + self.tier_definition.company_id = self.env.company + self.env.company.tier_validation_keep_review_history = False + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + self._reject_and_restart(record) + self.assertFalse(record.review_ids) + self.assertFalse(record.review_history_ids) + + # --- Reset paths -------------------------------------------------------- + + def test_pending_review_deleted(self): + """Pending (never acted on) reviews are deleted even when keeping.""" + self.tier_definition.keep_review_history = "keep" + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + record.restart_validation() + self.assertFalse(record.review_ids) + self.assertFalse(record.review_history_ids) + + def test_reset_to_cancel_archives_completed(self): + """The write reset path (confirm -> cancel) archives completed reviews.""" + self.tier_definition.keep_review_history = "keep" + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + record.validate_tier() + approved_review = record.review_ids + record.action_confirm() + record.write({"state": "cancel"}) + self.assertFalse(record.review_ids) + archived = record.review_history_ids + self.assertEqual(archived, approved_review) + self.assertEqual(archived.status, "approved") + + def test_mixed_rules_archive_and_delete(self): + """One reset, two rules: keep archived, no_keep deleted.""" + self.tier_definition.keep_review_history = "keep" + no_keep_def = 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)]", + "sequence": 40, + "keep_review_history": "no_keep", + "name": "Rule without history", + } + ) + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + self.assertEqual(len(record.review_ids), 2) + record.reject_tier() + record.restart_validation() + self.assertFalse(record.review_ids) + archived = record.review_history_ids + self.assertEqual(archived.definition_id, self.tier_definition) + self.assertNotIn(no_keep_def, archived.definition_id) + self.assertEqual(len(archived), 1) + + def test_deleted_definition_review_is_removed(self): + """A completed review whose definition was deleted must not break the reset. + + definition_id is ondelete="set null", so there is no rule left to resolve; + the review is removed like before the module.""" + self.tier_definition.keep_review_history = "keep" + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + record.reject_tier() + review = record.review_ids + self.tier_definition.unlink() + self.assertFalse(review.definition_id) + record.restart_validation() + self.assertFalse(review.exists()) + self.assertFalse(record.review_history_ids) + + def test_history_excludes_other_model_reviews(self): + """res_id is a Many2oneReference, so only the domain keeps models apart.""" + self.tier_definition.keep_review_history = "keep" + foreign_review = self.env["tier.review"].create( + { + "model": self.test_model_2._name, + "res_id": self.test_record.id, + "definition_id": self.tier_definition.id, + "status": "approved", + "active": False, + } + ) + self.test_record.with_user(self.test_user_2.id).request_validation() + record = self.test_record.with_user(self.test_user_1.id) + review = self._reject_and_restart(record) + self.assertEqual(record.review_history_ids, review) + self.assertNotIn(foreign_review, record.review_history_ids) + + def test_record_reference(self): + """record_reference links to the document and tolerates stale models.""" + review = self.env["tier.review"].create( + { + "model": self.test_model._name, + "res_id": self.test_record.id, + "definition_id": self.tier_definition.id, + } + ) + self.assertEqual(review.record_reference, self.test_record) + # History rows can outlive their model (module uninstalled). + review.model = "no.longer.installed" + self.assertFalse(review.record_reference) + + def test_host_unlink_removes_archived_reviews(self): + """Deleting the host record deletes all reviews, incl. archived history.""" + self.tier_definition.keep_review_history = "keep" + record_to_delete = self.test_model.create({"test_field": 2.5}) + record_to_delete.with_user(self.test_user_2.id).request_validation() + record = record_to_delete.with_user(self.test_user_1.id) + record.reject_tier() + record.restart_validation() + archived = record.review_history_ids + self.assertEqual(len(archived), 1) + record_to_delete.unlink() + self.assertFalse(archived.exists()) + + def test_history_block_injected_in_form_view(self): + """The review-history block is injected into the document form view. + + Uses test_record_2 (auto-injected, _tier_validation_manual_config=False). + Exercises _add_tier_validation_reviews (QWeb render +
wrap) and the + review_history_ids field so a template typo or render error is caught at + view-build time instead of only in the browser.""" + with Form(self.test_record_2) as f: + form = etree.fromstring(f._view["arch"]) + self.assertTrue(form.xpath("//field[@name='review_history_ids']")) diff --git a/base_tier_validation_review_history/views/res_config_settings_views.xml b/base_tier_validation_review_history/views/res_config_settings_views.xml new file mode 100644 index 00000000..0baae148 --- /dev/null +++ b/base_tier_validation_review_history/views/res_config_settings_views.xml @@ -0,0 +1,33 @@ + + + + res.config.settings.view.form.tier.validation.history + res.config.settings + + + +
+
+ +
+
+
+
+
+
+
+
diff --git a/base_tier_validation_review_history/views/tier_definition_view.xml b/base_tier_validation_review_history/views/tier_definition_view.xml new file mode 100644 index 00000000..fb13ada6 --- /dev/null +++ b/base_tier_validation_review_history/views/tier_definition_view.xml @@ -0,0 +1,15 @@ + + + + tier.definition.form.history + tier.definition + + + + + + + + + + diff --git a/base_tier_validation_review_history/views/tier_review_view.xml b/base_tier_validation_review_history/views/tier_review_view.xml new file mode 100644 index 00000000..84adc070 --- /dev/null +++ b/base_tier_validation_review_history/views/tier_review_view.xml @@ -0,0 +1,103 @@ + + + + + tier.review.tree.history + tier.review + + + + + + + + + + + + + + + + + tier.review.search.history + tier.review + + + + + + + + + + + + + + + + + + + + + + + Tier Reviews + ir.actions.act_window + tier.review + tree + + + {'active_test': False} + + + diff --git a/setup/base_tier_validation_review_history/odoo/addons/base_tier_validation_review_history b/setup/base_tier_validation_review_history/odoo/addons/base_tier_validation_review_history new file mode 120000 index 00000000..10a0357f --- /dev/null +++ b/setup/base_tier_validation_review_history/odoo/addons/base_tier_validation_review_history @@ -0,0 +1 @@ +../../../../base_tier_validation_review_history \ No newline at end of file diff --git a/setup/base_tier_validation_review_history/setup.py b/setup/base_tier_validation_review_history/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/base_tier_validation_review_history/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)