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
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

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.
@@ -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}