Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion document_page_approval/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion document_page_approval/hooks.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# Copyright 2018 Ivan Todorovich (<ivan.todorovich@gmail.com>)
# 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'"
Expand Down
2 changes: 1 addition & 1 deletion document_page_approval/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from . import document_page_approval, document_page_history_workflow
from . import document_page, document_page_history
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ast import literal_eval


class DocumentPageApproval(models.Model):
class DocumentPage(models.Model):
"""Useful to know the state of a document."""

_inherit = 'document.page'
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -100,14 +105,17 @@ def can_user_approve_this_page(self, user):
# 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
Expand All @@ -119,10 +127,19 @@ def _compute_has_changes_pending_approval(self):
('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):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# Copyright (C) 2013 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
# 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'
Expand All @@ -20,8 +18,8 @@ class DocumentPageHistoryWorkflow(models.Model):
('approved', 'Approved'),
('cancelled', 'Cancelled')],
'Status',
default='draft',
readonly=True,
default='draft'
)

approved_date = fields.Datetime(
Expand All @@ -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(
Expand All @@ -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'})

@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()
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 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
Expand All @@ -120,7 +129,7 @@ def page_approval_approved(self):
)

@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:
Expand All @@ -131,19 +140,18 @@ def page_approval_cancelled(self):
) % (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."""
Expand Down
21 changes: 21 additions & 0 deletions document_page_approval/security/document_page_security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,25 @@
<field name="implied_ids" eval="[(4, ref('group_document_approver_user'))]"/>
</record>

<record model="ir.rule" id="rule_change_request_global">
<field name="name">Change Request Global</field>
<field name="model_id" ref="model_document_page_history"/>
<field name="domain_force">['|',('state','=','approved'),('create_uid','=',user.id)]</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="perm_create" eval="True"/>
</record>

<record model="ir.rule" id="rule_change_request_approver">
<field name="name">Change Request Approver</field>
<field name="model_id" ref="model_document_page_history"/>
<field name="groups" eval="[(6, 0, [ref('group_document_approver_user')])]"/>
<field name="domain_force">[('state','!=','draft')]</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="perm_create" eval="True"/>
</record>

</odoo>
14 changes: 7 additions & 7 deletions document_page_approval/tests/test_document_page_approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_change_request_approve(self):
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)

Expand All @@ -58,7 +58,7 @@ def test_change_request_approve(self):
('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):
Expand All @@ -74,7 +74,7 @@ def test_change_request_from_scratch(self):
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({
Expand All @@ -88,25 +88,25 @@ def test_change_request_from_scratch(self):
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)
Expand Down
Loading