From a8346c25f3b4e4ee9bb1c2da01324da695a81ea6 Mon Sep 17 00:00:00 2001 From: Bhavesh Heliconia Date: Fri, 28 Mar 2025 16:27:39 +0530 Subject: [PATCH] [MIG] document_page_approval: Migration to 18.0 --- document_page_approval/README.rst | 34 +-- document_page_approval/__manifest__.py | 2 +- .../models/document_page_history.py | 11 +- document_page_approval/readme/CONTRIBUTORS.md | 2 + .../static/description/index.html | 11 +- .../tests/test_document_page_approval.py | 217 ++++++++++++++---- .../views/document_page_approval.xml | 19 +- 7 files changed, 212 insertions(+), 84 deletions(-) diff --git a/document_page_approval/README.rst b/document_page_approval/README.rst index 84f8b16f..51f790fa 100644 --- a/document_page_approval/README.rst +++ b/document_page_approval/README.rst @@ -17,13 +17,13 @@ Document Page Approval :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github - :target: https://github.com/OCA/knowledge/tree/17.0/document_page_approval + :target: https://github.com/OCA/knowledge/tree/18.0/document_page_approval :alt: OCA/knowledge .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/knowledge-17-0/knowledge-17-0-document_page_approval + :target: https://translation.odoo-community.org/projects/knowledge-18-0/knowledge-18-0-document_page_approval :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/knowledge&target_branch=17.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/knowledge&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -64,7 +64,7 @@ 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -79,24 +79,28 @@ Authors Contributors ------------ -- Odoo SA +- Odoo SA -- Savoir-faire Linux +- Savoir-faire Linux -- Gervais Naoussi +- Gervais Naoussi -- Maxime Chambreuil +- Maxime Chambreuil -- Iván Todorovich +- Iván Todorovich -- `Tecnativa `__: +- `Tecnativa `__: - - Victor M.M. Torres - - Víctor Martínez + - Victor M.M. Torres + - Víctor Martínez -- `Guadaltech `__: +- `Guadaltech `__: - - Fernando La Chica + - Fernando La Chica + +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia Maintainers ----------- @@ -111,6 +115,6 @@ 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/knowledge `_ project on GitHub. +This module is part of the `OCA/knowledge `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/document_page_approval/__manifest__.py b/document_page_approval/__manifest__.py index deac0fa1..d6f4c9e3 100644 --- a/document_page_approval/__manifest__.py +++ b/document_page_approval/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Document Page Approval", - "version": "17.0.1.0.0", + "version": "18.0.1.0.0", "author": "Savoir-faire Linux, Odoo Community Association (OCA)", "website": "https://github.com/OCA/knowledge", "license": "AGPL-3", diff --git a/document_page_approval/models/document_page_history.py b/document_page_approval/models/document_page_history.py index 84a0ebc4..085fb591 100644 --- a/document_page_approval/models/document_page_history.py +++ b/document_page_approval/models/document_page_history.py @@ -1,6 +1,7 @@ # Copyright (C) 2013 Savoir-faire Linux (). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from odoo import fields, models from odoo.exceptions import UserError from odoo.tools.translate import _ @@ -93,12 +94,14 @@ class DocumentPageHistory(models.Model): raise UserError( _( "You are not authorized to do this.\r\n" - "Only approvers with these groups can approve this: " - ) - % ", ".join( - [g.display_name for g in rec.page_id.approver_group_ids] + "Only approvers with these groups can approve this: {}" + ).format( + ", ".join( + [g.display_name for g in rec.page_id.approver_group_ids] + ) ) ) + # Update state rec.write( { diff --git a/document_page_approval/readme/CONTRIBUTORS.md b/document_page_approval/readme/CONTRIBUTORS.md index 52c3c13d..6cc0b9a9 100644 --- a/document_page_approval/readme/CONTRIBUTORS.md +++ b/document_page_approval/readme/CONTRIBUTORS.md @@ -16,3 +16,5 @@ - [Guadaltech](https://www.guadaltech.es): - Fernando La Chica \<\> +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia diff --git a/document_page_approval/static/description/index.html b/document_page_approval/static/description/index.html index 2ef088d0..68cf4ff5 100644 --- a/document_page_approval/static/description/index.html +++ b/document_page_approval/static/description/index.html @@ -369,7 +369,7 @@ ul.auto-toc { !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:4b3ef803acd19a66515436d838c979c63daecf130c34b3f628dd5d8ac8137d34 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

+

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

This module adds a workflow to approve page modifications and show the approved version by default.

Table of contents

@@ -413,7 +413,7 @@ history to review.

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.

+feedback.

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

@@ -450,6 +450,11 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
  • Fernando La Chica <fernando.lachica@guadaltech.es>
  • +
  • Heliconia Solutions Pvt. Ltd.

    +
      +
    • Bhavesh Heliconia
    • +
    +
  • @@ -461,7 +466,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome

    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/knowledge project on GitHub.

    +

    This module is part of the OCA/knowledge project on GitHub.

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

    diff --git a/document_page_approval/tests/test_document_page_approval.py b/document_page_approval/tests/test_document_page_approval.py index 9e250e2e..375fe746 100644 --- a/document_page_approval/tests/test_document_page_approval.py +++ b/document_page_approval/tests/test_document_page_approval.py @@ -1,3 +1,5 @@ +from odoo import Command +from odoo.exceptions import UserError from odoo.tests import new_test_user from odoo.addons.base.tests.common import BaseCommon @@ -9,18 +11,25 @@ class TestDocumentPageApproval(BaseCommon): super().setUpClass() cls.page_obj = cls.env["document.page"] cls.history_obj = cls.env["document.page.history"] - # demo + + # Demo Data cls.category1 = cls.env.ref("document_page.demo_category1") cls.page1 = cls.env.ref("document_page.demo_page1") + + # Create test user without groups first cls.user2 = new_test_user( cls.env, login="test-user2", - groups="base.group_user,document_page_approval.group_document_approver_user", + groups="document_page_approval.group_document_approver_user", ) + + # Ensure user2 has the approver group cls.approver_gid = cls.env.ref( "document_page_approval.group_document_approver_user" ) - # demo_approval + cls.user2.write({"groups_id": [Command.link(cls.approver_gid.id)]}) + + # Create category and page that require approval cls.category2 = cls.page_obj.create( { "name": "This category requires approval", @@ -38,62 +47,59 @@ class TestDocumentPageApproval(BaseCommon): ) def test_approval_required(self): + """Test that the page requires approval.""" page = self.page2 self.assertTrue(page.is_approval_required) self.assertTrue(page.has_changes_pending_approval) self.assertEqual(len(page.history_ids), 0) def test_change_request_approve(self): + """Test that an approver can approve a change request.""" page = self.page2 + + # Get the change request for this page chreq = self.history_obj.search( - [("page_id", "=", page.id), ("state", "!=", "approved")] - )[0] + [("page_id", "=", page.id), ("state", "!=", "approved")], limit=1 + ) - # It should automatically be in 'to approve' state self.assertEqual(chreq.state, "to approve") - user_admin = self.env.ref("base.user_admin") - self.assertTrue(user_admin.partner_id.id in chreq.message_partner_ids.ids) - self.assertTrue(self.user2.partner_id.id in chreq.message_partner_ids.ids) - # Needed to compute calculated fields - page.invalidate_model() - self.assertNotEqual(chreq.content, page.content) + # Ensure user2 is listed as an approver + self.assertTrue(chreq.with_user(self.user2).am_i_approver) - # who_am_i - self.assertTrue(chreq.am_i_owner) - self.assertTrue(chreq.am_i_approver) - - # approve - chreq.action_approve() + # Approve the request as user2 (approver) + chreq.with_user(self.user2).action_approve() self.assertEqual(chreq.state, "approved") self.assertEqual(chreq.content, page.content) - # new changes should create change requests + # Create new change request page.write({"content": "New content"}) - # Needed to compute calculated fields - page.invalidate_model() - self.assertNotEqual(page.content, "New content") + page.invalidate_model() # Recompute fields chreq = self.history_obj.search( - [("page_id", "=", page.id), ("state", "!=", "approved")] - )[0] - chreq.action_approve() + [("page_id", "=", page.id), ("state", "!=", "approved")], limit=1 + ) + + # Approve new changes + chreq.with_user(self.user2).action_approve() self.assertEqual(page.content, "New content") def test_change_request_auto_approve(self): + """Test that a page without approval required auto-approves changes.""" page = self.page1 self.assertFalse(page.is_approval_required) page.write({"content": "New content"}) self.assertEqual(page.content, "New content") def test_change_request_from_scratch(self): + """Test a full change request lifecycle from draft to approval.""" page = self.page2 - # aprove everything + # Approve all pending change requests self.history_obj.search( [("page_id", "=", page.id), ("state", "!=", "approved")] - ).action_approve() + ).with_user(self.user2).action_approve() - # new change request from scrath + # Create new change request chreq = self.history_obj.create( { "page_id": page.id, @@ -103,41 +109,152 @@ class TestDocumentPageApproval(BaseCommon): ) self.assertEqual(chreq.state, "draft") - self.assertNotEqual(page.content, chreq.content) - self.assertNotEqual(page.approved_date, chreq.approved_date) - self.assertNotEqual(page.approved_uid, chreq.approved_uid) + chreq.action_to_approve() + self.assertEqual(chreq.state, "to approve") + + # Cancel and return to draft + chreq.with_user(self.user2).action_cancel() + self.assertEqual(chreq.state, "cancelled") + + chreq.with_user(self.user2).action_draft() + self.assertEqual(chreq.state, "draft") chreq.action_to_approve() self.assertEqual(chreq.state, "to approve") - self.assertNotEqual(page.content, chreq.content) - self.assertNotEqual(page.approved_date, chreq.approved_date) - self.assertNotEqual(page.approved_uid, chreq.approved_uid) - - chreq.action_cancel() - self.assertEqual(chreq.state, "cancelled") - self.assertNotEqual(page.content, chreq.content) - self.assertNotEqual(page.approved_date, chreq.approved_date) - self.assertNotEqual(page.approved_uid, chreq.approved_uid) - - chreq.action_draft() - self.assertEqual(chreq.state, "draft") - self.assertNotEqual(page.content, chreq.content) - self.assertNotEqual(page.approved_date, chreq.approved_date) - self.assertNotEqual(page.approved_uid, chreq.approved_uid) - - chreq.action_approve() + chreq.with_user(self.user2).action_approve() self.assertEqual(chreq.state, "approved") self.assertEqual(page.content, chreq.content) self.assertEqual(page.approved_date, chreq.approved_date) self.assertEqual(page.approved_uid, chreq.approved_uid) def test_get_approvers_guids(self): - """Get approver guids.""" + """Test that approver groups are properly assigned.""" page = self.page2 self.assertTrue(len(page.approver_group_ids) > 0) def test_get_page_url(self): - """Test if page url exist.""" + """Test that the page URL exists.""" pages = self.env["document.page.history"].search([]) page = pages[0] self.assertIsNotNone(page.page_url) + + def test_compute_is_approval_required(self): + """Ensure approval rules are inherited correctly""" + self.assertTrue(self.page2.is_approval_required) + self.page2.parent_id.approval_required = False + self.page2.invalidate_model() + self.assertFalse(self.page2.is_approval_required) + + def test_compute_approver_group_ids(self): + """Ensure approver groups are inherited correctly""" + self.assertIn(self.approver_gid, self.page2.approver_group_ids) + self.page2.parent_id.approver_gid = False + self.page2.invalidate_model() + self.assertFalse(self.page2.approver_group_ids) + + def test_can_user_approve_this_page(self): + """Check different approval conditions""" + self.assertTrue( + self.page2.with_user(self.user2).can_user_approve_this_page(self.user2) + ) + + # Remove approval group from user2 + self.user2.write({"groups_id": [(3, self.approver_gid.id)]}) + self.assertFalse( + self.page2.with_user(self.user2).can_user_approve_this_page(self.user2) + ) + + def test_pending_approval_detection(self): + """Ensure the system detects pending approval changes""" + # Reset page2 by removing previous history + self.history_obj.search([("page_id", "=", self.page2.id)]).unlink() + + self.page2.invalidate_model() + self.assertFalse(self.page2.has_changes_pending_approval) + + # Create a new change request + self.history_obj.create( + { + "page_id": self.page2.id, + "state": "to approve", + } + ) + + self.page2.invalidate_model() + self.assertTrue(self.page2.has_changes_pending_approval) + + def test_user_has_drafts(self): + """Ensure the system detects drafts correctly""" + self.page2.invalidate_model() + self.assertFalse(self.page2.user_has_drafts) + + self.history_obj.create( + { + "page_id": self.page2.id, + "state": "draft", + } + ) + self.page2.invalidate_model() + self.assertTrue(self.page2.user_has_drafts) + + def test_action_draft_requires_cancellation(self): + """Ensure a change request must be cancelled before setting to draft""" + chreq = self.history_obj.create( + { + "page_id": self.page2.id, + "state": "to approve", + } + ) + with self.assertRaises(UserError): + chreq.action_draft() + + def test_action_to_approve_only_from_draft(self): + """Ensure only draft requests can be sent for approval""" + chreq = self.history_obj.create( + { + "page_id": self.page2.id, + "state": "approved", + } + ) + with self.assertRaises(UserError): + chreq.action_to_approve() + + def test_approval_permission_check(self): + """Ensure approval is restricted to approvers""" + chreq = self.history_obj.create( + { + "page_id": self.page2.id, + "state": "to approve", + } + ) + + with self.assertRaises(UserError): + chreq.with_user(self.env.ref("base.user_demo")).action_approve() + + # Grant approval rights + chreq.with_user(self.user2).action_approve() + self.assertEqual(chreq.state, "approved") + + def test_page_url_computation(self): + """Ensure page URLs are generated correctly""" + chreq = self.history_obj.create({"page_id": self.page2.id}) + self.assertIn("web#db=", chreq.page_url) + + def test_diff_computation(self): + """Ensure document diff is calculated properly""" + self.history_obj.create( + { + "page_id": self.page2.id, + "content": "Version 1", + "state": "approved", + } + ) + + chreq2 = self.history_obj.create( + { + "page_id": self.page2.id, + "content": "Version 2", + } + ) + chreq2._compute_diff() + self.assertIsNotNone(chreq2.diff) diff --git a/document_page_approval/views/document_page_approval.xml b/document_page_approval/views/document_page_approval.xml index d05eeeef..87cdf3c4 100644 --- a/document_page_approval/views/document_page_approval.xml +++ b/document_page_approval/views/document_page_approval.xml @@ -90,10 +90,7 @@ -
    - - -
    +
    @@ -178,13 +175,13 @@ - + - + @@ -224,16 +221,16 @@ document.page.history - + state=='draft' state=='to approve' state=='cancelled' - - + + - + @@ -283,7 +280,7 @@ Change Requests document.page.history - tree,form + list,form {'search_default_draft': 1, 'search_default_pending': 1}