diff --git a/document_page_approval/__manifest__.py b/document_page_approval/__manifest__.py index 61a39bfc..46aaf9eb 100644 --- a/document_page_approval/__manifest__.py +++ b/document_page_approval/__manifest__.py @@ -3,7 +3,7 @@ { 'name': 'Document Page Approval', - 'version': '11.0.1.0.0', + 'version': '11.0.2.0.0', "author": "Savoir-faire Linux, Odoo Community Association (OCA)", "website": "http://www.savoirfairelinux.com", "license": "AGPL-3", diff --git a/document_page_approval/hooks.py b/document_page_approval/hooks.py index 212e6583..973032d6 100644 --- a/document_page_approval/hooks.py +++ b/document_page_approval/hooks.py @@ -1,20 +1,24 @@ # Copyright 2018 Ivan Todorovich () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging +_logger = logging.getLogger(__name__) def post_init_hook(cr, registry): # pragma: no cover # Set all pre-existing pages history to approved + _logger.info('Setting history to approved.') cr.execute(""" UPDATE document_page_history SET state='approved', approved_uid=create_uid, approved_date=create_date - WHERE state IS NULL + WHERE state IS NULL OR state = 'draft' """) def uninstall_hook(cr, registry): # pragma: no cover # Remove unapproved pages + _logger.info('Deleting unapproved Change Requests.') cr.execute( "DELETE FROM document_page_history " "WHERE state != 'approved'" diff --git a/document_page_approval/i18n/document_page_approval.pot b/document_page_approval/i18n/document_page_approval.pot index 1f7930ee..73771062 100644 --- a/document_page_approval/i18n/document_page_approval.pot +++ b/document_page_approval/i18n/document_page_approval.pot @@ -46,6 +46,12 @@ msgid "\n" " " msgstr "" +#. module: document_page_approval +#: model:ir.ui.view,arch_db:document_page_approval.wiki_form_inherit +msgid "You already have a Draft Change Request for this page.\n" +" It is highly recommended that you edit that one instead of creating a new one." +msgstr "" + #. module: document_page_approval #: model:ir.model.fields,field_description:document_page_approval.field_document_page_am_i_approver #: model:ir.model.fields,field_description:document_page_approval.field_document_page_history_am_i_approver @@ -107,6 +113,18 @@ msgstr "" msgid "Back to draft" msgstr "" +#. module: document_page_approval +#: code:addons/document_page_approval/models/document_page_history.py:100 +#, python-format +msgid "Can't approve page in '%s' state." +msgstr "" + +#. module: document_page_approval +#: code:addons/document_page_approval/models/document_page_history.py:76 +#, python-format +msgid "Can't approve pages in '%s' state." +msgstr "" + #. module: document_page_approval #: model:ir.ui.view,arch_db:document_page_approval.wiki_history_form_inherit msgid "Cancel" @@ -126,13 +144,13 @@ msgid "Change Requests" msgstr "" #. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:129 +#: code:addons/document_page_approval/models/document_page_history.py:138 #, python-format msgid "Change request %s has been cancelled by %s." msgstr "" #. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:110 +#: code:addons/document_page_approval/models/document_page_history.py:119 #, python-format msgid "Change request has been approved by %s." msgstr "" @@ -169,25 +187,13 @@ msgstr "" msgid "If true, changes of this page require approval" msgstr "" -#. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:59 -#, python-format -msgid "It's not cancelled or approved" -msgstr "" - -#. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:71 -#, python-format -msgid "It's not in draft state" -msgstr "" - #. module: document_page_approval #: model:mail.template,subject:document_page_approval.email_template_new_draft_need_approval msgid "New version of ${object.display_name} needs your approval" msgstr "" #. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:117 +#: code:addons/document_page_approval/models/document_page_history.py:126 #, python-format msgid "New version of the document %s approved." msgstr "" @@ -238,14 +244,40 @@ msgstr "" msgid "URL" msgstr "" +#. module: document_page_approval +#: model:ir.model.fields,field_description:document_page_approval.field_document_page_user_has_drafts +msgid "User has drafts?" +msgstr "" + #. module: document_page_approval #: model:ir.model.fields,help:document_page_approval.field_document_page_approver_gid msgid "Users must also belong to the Approvers group" msgstr "" #. module: document_page_approval -#: code:addons/document_page_approval/models/document_page_history_workflow.py:62 +#: code:addons/document_page_approval/models/document_page_history.py:102 #, python-format -msgid "You are not an appover to reset to draft" +msgid "You are not authorized to do this. \n" +"Only approvers with these groups can approve this: " +msgstr "" + +#. module: document_page_approval +#: code:addons/document_page_approval/models/document_page_history.py:62 +#, python-format +msgid "You are not authorized to do this. \n" +"Only owners or approvers can reopen Change Requests." +msgstr "" + +#. module: document_page_approval +#: code:addons/document_page_approval/models/document_page_history.py:79 +#, python-format +msgid "You are not authorized to do this. \n" +"Only owners or approvers can request approval." +msgstr "" + +#. module: document_page_approval +#: code:addons/document_page_approval/models/document_page_history.py:59 +#, python-format +msgid "You need to cancel it before reopening." msgstr "" diff --git a/document_page_approval/models/__init__.py b/document_page_approval/models/__init__.py index e1b9d5b5..a382c9e1 100644 --- a/document_page_approval/models/__init__.py +++ b/document_page_approval/models/__init__.py @@ -1 +1 @@ -from . import document_page_approval, document_page_history_workflow +from . import document_page, document_page_history diff --git a/document_page_approval/models/document_page_approval.py b/document_page_approval/models/document_page.py similarity index 79% rename from document_page_approval/models/document_page_approval.py rename to document_page_approval/models/document_page.py index 370b4407..44c881c8 100644 --- a/document_page_approval/models/document_page_approval.py +++ b/document_page_approval/models/document_page.py @@ -6,7 +6,7 @@ from odoo import api, fields, models from ast import literal_eval -class DocumentPageApproval(models.Model): +class DocumentPage(models.Model): """Useful to know the state of a document.""" _inherit = 'document.page' @@ -66,6 +66,11 @@ class DocumentPageApproval(models.Model): string='Has changes pending approval' ) + user_has_drafts = fields.Boolean( + compute='_compute_user_has_drafts', + string='User has drafts?', + ) + @api.multi @api.depends('approval_required', 'parent_id.is_approval_required') def _compute_is_approval_required(self): @@ -100,14 +105,17 @@ class DocumentPageApproval(models.Model): # if it's not required, anyone can approve if not self.is_approval_required: return True - # to approve, you must have approver rights - approver_group_id = self.env.ref( - 'document_page_approval.group_document_approver_user') - if approver_group_id not in user.groups_id: + # if user belongs to 'Knowledge / Manager', he can approve anything + if user.has_group('document_page.group_document_manager'): + return True + # to approve, user must have approver rights + if not user.has_group( + 'document_page_approval.group_document_approver_user'): return False - # and belong to at least one of the approver_groups (if any is set) + # if there aren't any approver_groups_defined, user can approve if not self.approver_group_ids: return True + # to approve, user must belong to any of the approver groups return len(user.groups_id & self.approver_group_ids) > 0 @api.multi @@ -119,10 +127,19 @@ class DocumentPageApproval(models.Model): ('state', '=', 'to approve')]) rec.has_changes_pending_approval = (changes > 0) + @api.multi + def _compute_user_has_drafts(self): + history = self.env['document.page.history'] + for rec in self: + changes = history.search_count([ + ('page_id', '=', rec.id), + ('state', '=', 'draft')]) + rec.user_has_drafts = (changes > 0) + @api.multi def _create_history(self, vals): - res = super(DocumentPageApproval, self)._create_history(vals) - res.document_page_auto_confirm() + res = super(DocumentPage, self)._create_history(vals) + res.action_to_approve() @api.multi def action_changes_pending_approval(self): diff --git a/document_page_approval/models/document_page_history_workflow.py b/document_page_approval/models/document_page_history.py similarity index 67% rename from document_page_approval/models/document_page_history_workflow.py rename to document_page_approval/models/document_page_history.py index 3944afec..fd9bfc96 100644 --- a/document_page_approval/models/document_page_history_workflow.py +++ b/document_page_approval/models/document_page_history.py @@ -1,14 +1,12 @@ # Copyright (C) 2013 Savoir-faire Linux (). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from datetime import datetime from odoo.tools.translate import _ -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo import api, fields, models from odoo.exceptions import UserError -class DocumentPageHistoryWorkflow(models.Model): +class DocumentPageHistory(models.Model): """Useful to manage edition's workflow on a document.""" _name = 'document.page.history' @@ -20,8 +18,8 @@ class DocumentPageHistoryWorkflow(models.Model): ('approved', 'Approved'), ('cancelled', 'Cancelled')], 'Status', + default='draft', readonly=True, - default='draft' ) approved_date = fields.Datetime( @@ -43,7 +41,8 @@ class DocumentPageHistoryWorkflow(models.Model): ) am_i_approver = fields.Boolean( - compute='_compute_am_i_approver' + related='page_id.am_i_approver', + related_sudo=False, ) page_url = fields.Text( @@ -52,56 +51,66 @@ class DocumentPageHistoryWorkflow(models.Model): ) @api.multi - def page_approval_draft(self): + def action_draft(self): """Set a change request as draft""" - if self.filtered(lambda r: r.state not in [ - 'cancelled', 'approved']): - raise UserError(_("It's not cancelled or approved")) - if self.filtered(lambda r: - r.state == 'approved' and not self.am_i_approver): - raise UserError(_("You are not an appover to reset to draft")) - self.write({'state': 'draft'}) + for rec in self: + if not rec.state == 'cancelled': + raise UserError( + _('You need to cancel it before reopening.')) + if not (rec.am_i_owner or rec.am_i_approver): + raise UserError( + _('You are not authorized to do this.\r\n' + 'Only owners or approvers can reopen Change Requests.')) + rec.write({'state': 'draft'}) @api.multi - def document_page_auto_confirm(self): - """Automatic Transitions for change requests created directly from - documents - """ - if self.filtered(lambda r: r.state != 'draft'): - raise UserError(_("It's not in draft state")) - to_approve = self.filtered(lambda r: r.is_approval_required) - to_approve.write({'state': 'to approve'}) - approved = (self - to_approve) - approved.write({'state': 'approved'}) - approved.mapped('page_id')._compute_history_head() - - @api.multi - def page_approval_to_approve(self): + def action_to_approve(self): """Set a change request as to approve""" - self.write({'state': 'to approve'}) template = self.env.ref( 'document_page_approval.email_template_new_draft_need_approval') approver_gid = self.env.ref( 'document_page_approval.group_document_approver_user') for rec in self: + if rec.state != 'draft': + raise UserError( + _("Can't approve pages in '%s' state.") % rec.state) + if not (rec.am_i_owner or rec.am_i_approver): + raise UserError( + _('You are not authorized to do this.\r\n' + 'Only owners or approvers can request approval.')) + # request approval if rec.is_approval_required: + rec.write({'state': 'to approve'}) guids = [g.id for g in rec.page_id.approver_group_ids] users = self.env['res.users'].search([ ('groups_id', 'in', guids), ('groups_id', 'in', approver_gid.id)]) rec.message_subscribe_users([u.id for u in users]) rec.message_post_with_template(template.id) + else: + # auto-approve if approval is not required + rec.action_approve() @api.multi - def page_approval_approved(self): + def action_approve(self): """Set a change request as approved.""" - self.write({ - 'state': 'approved', - 'approved_date': datetime.now().strftime( - DEFAULT_SERVER_DATETIME_FORMAT), - 'approved_uid': self.env.uid - }) for rec in self: + if rec.state not in ['draft', 'to approve']: + raise UserError( + _("Can't approve page in '%s' state.") % rec.state) + if not rec.am_i_approver: + 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])) + # Update state + rec.write({ + 'state': 'approved', + 'approved_date': fields.datetime.now(), + 'approved_uid': self.env.uid, + }) # Trigger computed field update rec.page_id._compute_history_head() # Notify state change @@ -120,7 +129,7 @@ class DocumentPageHistoryWorkflow(models.Model): ) @api.multi - def page_approval_cancelled(self): + def action_cancel(self): """Set a change request as cancelled.""" self.write({'state': 'cancelled'}) for rec in self: @@ -131,19 +140,18 @@ class DocumentPageHistoryWorkflow(models.Model): ) % (rec.display_name, self.env.user.name) ) + @api.multi + def action_cancel_and_draft(self): + """Set a change request as draft, cancelling it first""" + self.action_cancel() + self.action_draft() + @api.multi def _compute_am_i_owner(self): """Check if current user is the owner""" for rec in self: rec.am_i_owner = (rec.create_uid == self.env.user) - @api.multi - def _compute_am_i_approver(self): - """check if current user is a approver""" - for rec in self: - rec.am_i_approver = rec.page_id.can_user_approve_this_page( - self.env.user) - @api.multi def _compute_page_url(self): """Compute the page url.""" diff --git a/document_page_approval/security/document_page_security.xml b/document_page_approval/security/document_page_security.xml index ab80c13f..ab79d1ef 100644 --- a/document_page_approval/security/document_page_security.xml +++ b/document_page_approval/security/document_page_security.xml @@ -11,4 +11,25 @@ + + Change Request Global + + ['|',('state','=','approved'),('create_uid','=',user.id)] + + + + + + + + Change Request Approver + + + [('state','!=','draft')] + + + + + + diff --git a/document_page_approval/tests/test_document_page_approval.py b/document_page_approval/tests/test_document_page_approval.py index 8fcd73e9..12cdfdbf 100644 --- a/document_page_approval/tests/test_document_page_approval.py +++ b/document_page_approval/tests/test_document_page_approval.py @@ -47,7 +47,7 @@ class TestDocumentPageApproval(common.TransactionCase): self.assertTrue(chreq.am_i_approver) # approve - chreq.page_approval_approved() + chreq.action_approve() self.assertEqual(chreq.state, 'approved') self.assertEqual(chreq.content, page.content) @@ -58,7 +58,7 @@ class TestDocumentPageApproval(common.TransactionCase): ('page_id', '=', page.id), ('state', '!=', 'approved') ])[0] - chreq.page_approval_approved() + chreq.action_approve() self.assertEqual(page.content, 'New content') def test_change_request_auto_approve(self): @@ -74,7 +74,7 @@ class TestDocumentPageApproval(common.TransactionCase): self.history_obj.search([ ('page_id', '=', page.id), ('state', '!=', 'approved') - ]).page_approval_approved() + ]).action_approve() # new change request from scrath chreq = self.history_obj.create({ @@ -88,25 +88,25 @@ class TestDocumentPageApproval(common.TransactionCase): self.assertNotEqual(page.approved_date, chreq.approved_date) self.assertNotEqual(page.approved_uid, chreq.approved_uid) - chreq.page_approval_to_approve() + 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.page_approval_cancelled() + 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.page_approval_draft() + 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.page_approval_approved() + chreq.action_approve() self.assertEqual(chreq.state, 'approved') self.assertEqual(page.content, chreq.content) self.assertEqual(page.approved_date, chreq.approved_date) diff --git a/document_page_approval/views/document_page_approval.xml b/document_page_approval/views/document_page_approval.xml index 3d7ec0cd..5f7759c6 100644 --- a/document_page_approval/views/document_page_approval.xml +++ b/document_page_approval/views/document_page_approval.xml @@ -10,20 +10,23 @@
- @@ -176,7 +198,7 @@ document.page.history form tree,form - {'search_default_state':'to approve'} + {'search_default_draft': 1, 'search_default_pending': 1}