[IMP][10.0][document_page_approval] Change Requests and workflow improvements on documents (#155)

This commit is contained in:
Iván Todorovich 2018-04-13 11:38:18 -03:00 committed by Gabriela Mogollon
parent 6f8fe0a8cf
commit ae6e3be82a
15 changed files with 677 additions and 425 deletions

View File

@ -58,6 +58,7 @@ Contributors
* Savoir-faire Linux <support@savoirfairelinux.com>
* Gervais Naoussi <gervaisnaoussi@gmail.com>
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
* Iván Todorovich <ivan.todorovich@gmail.com>
Maintainer
----------

View File

@ -3,3 +3,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
from .hooks import post_init_hook, uninstall_hook

View File

@ -4,7 +4,7 @@
{
'name': 'Document Page Approval',
'version': '10.0.1.1.0',
'version': '10.0.2.0.0',
"author": "Savoir-faire Linux, Odoo Community Association (OCA)",
"website": "http://www.savoirfairelinux.com",
"license": "AGPL-3",
@ -25,4 +25,6 @@
'images/page_history_list.png',
'images/page_history.png',
],
'post_init_hook': 'post_init_hook',
'uninstall_hook': 'uninstall_hook',
}

View File

@ -1,12 +1,11 @@
<?xml version="1.0"?>
<odoo noupdate="1">
<odoo>
<!-- Allow user to make upgrade-proof customizations to email template -->
<!-- If user wants to make upgrade-proof customizations to email templates, he should edit ir.model.data and check noupdate himself -->
<record id="email_template_new_draft_need_approval" model="mail.template">
<field name="name">Automated new draft need approval Notification Mail</field>
<field name="email_from">${object.create_uid.company_id.email or 'noreply@localhost.com'}</field>
<field name="subject">New version of "${object.page_id.name}" to approve</field>
<field name="email_to">${object.get_approvers_email}</field>
<field name="subject">New version of ${object.display_name} needs your approval</field>
<field name="model_id" ref="model_document_page_history"/>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.create_uid.partner_id.lang}</field>
@ -14,13 +13,30 @@
<![CDATA[
<p>Hello,</p>
<p>The page "${object.page_id.name}" has been modified and need your approval.</p>
<p>${object.create_uid.name} submited a new Change Request for <b>${object.page_id.name}</b> and it needs your approval.</p>
<p>You can review the new version here : <a href="${object.get_page_url}">${object.get_page_url}</a></p>
<h1><a href="${object.page_url}">${object.display_name}</a></h1>
<p>
<b>Modified by:</b> ${object.create_uid.name}<br/>
<b>Date:</b> ${object.create_date}<br>
</p>
% if object.summary:
<h3>Summary</h3>
<p>${object.summary}</p>
% endif
<h3>Diff</h3>
<div style="overflow-x:scroll; font-size:0.85em; margin-bottom:2em;">
${object.diff|safe}
</div>
<p>Have a great day.</p>
<p>Have a great day.<br/>
--<br/>
Odoo</p>]]>
<p>Odoo</p>
]]>
</field>
</record>

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Ivan Todorovich (<ivan.todorovich@gmail.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
def post_init_hook(cr, registry): # pragma: no cover
# Set all pre-existing pages history to approved
cr.execute("""
UPDATE document_page_history
SET state='approved',
approved_uid=create_uid,
approved_date=create_date
WHERE state IS NULL
""")
def uninstall_hook(cr, registry): # pragma: no cover
# Remove unapproved pages
cr.execute(
"DELETE FROM document_page_history "
"WHERE state != 'approved'"
)

View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Ivan Todorovich <ivan.todorovich@gmail.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
def migrate(cr, version): # pragma: no cover
# Set all pre-existing pages history to approved
cr.execute("""
UPDATE document_page_history
SET state='approved',
approved_uid=create_uid,
approved_date=create_date
WHERE state IS NULL
""")

View File

@ -2,7 +2,9 @@
# Copyright (C) 2013 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import api, fields, models
from odoo import api, fields, models
from ast import literal_eval
class DocumentPageApproval(models.Model):
@ -10,104 +12,126 @@ class DocumentPageApproval(models.Model):
_inherit = 'document.page'
@api.multi
def _get_display_content(self):
"""Display the content of document."""
for page in self:
content = ""
if page.type == "category":
content = self._get_page_index(page, link=False)
else:
history = self.env['document.page.history']
if self.is_approval_required(page):
history_ids = history.search(
[
('page_id', '=', page.id),
('state', '=', 'approved')
],
limit=1,
order='create_date DESC'
)
content = history_ids.content
else:
content = page.content
page.display_content = content
@api.multi
def _get_approved_date(self):
"""Return the approved date of a document."""
for page in self:
approved_date = False
if self.is_approval_required(page):
history = self.env['document.page.history']
history_ids = history.search(
[
('page_id', '=', page.id),
('state', '=', 'approved')
],
limit=1,
order='create_date DESC'
)
approved_date = history_ids.approved_date
page.approved_date = approved_date
@api.multi
def _get_approved_uid(self):
"""Return the user's id of the approved user."""
for page in self:
approved_uid = False
if self.is_approval_required(page):
history = self.env['document.page.history']
history_ids = history.search(
[
('page_id', '=', page.id),
('state', '=', 'approved')
],
limit=1,
order='create_date DESC'
)
approved_uid = history_ids.approved_uid.id
page.approved_uid = approved_uid
@api.multi
def _is_parent_approval_required(self):
"""Check if the document requires approval base on his parent."""
for page in self:
page.is_parent_approval_required = self.is_approval_required(page)
def is_approval_required(self, page):
"""Check if a document requires approval."""
if page:
res = page.approval_required
res = res or self.is_approval_required(page.parent_id)
else:
res = False
return res
display_content = fields.Text(
compute=_get_display_content,
string='Displayed Content'
history_ids = fields.One2many(
order='approved_date DESC',
domain=[('state', '=', 'approved')],
)
approved_date = fields.Datetime(
compute=_get_approved_date,
string="Approved Date"
'Approved Date',
related='history_head.approved_date',
store=True,
index=True,
readonly=True,
)
approved_uid = fields.Many2one(
'res.users',
compute=_get_approved_uid,
string="Approved By",
'Approved by',
related='history_head.approved_uid',
store=True,
index=True,
readonly=True,
)
approval_required = fields.Boolean("Require approval")
is_parent_approval_required = fields.Boolean(
compute=_is_parent_approval_required,
string="parent approval"
approval_required = fields.Boolean(
'Require approval',
help='Require approval for changes on this page or its child pages.',
)
approver_gid = fields.Many2one(
"res.groups",
"Approver group"
"Approver group",
help='Users must also belong to the Approvers group',
)
is_approval_required = fields.Boolean(
'Approval required',
help='If true, changes of this page require approval',
compute='_compute_is_approval_required',
)
am_i_approver = fields.Boolean(
compute='_compute_am_i_approver'
)
approver_group_ids = fields.Many2many(
'res.groups',
string='Approver groups',
help='Groups that can approve changes to this document',
compute='_compute_approver_group_ids',
)
has_changes_pending_approval = fields.Boolean(
compute='_compute_has_changes_pending_approval',
string='Has changes pending approval'
)
@api.multi
@api.depends('approval_required', 'parent_id.is_approval_required')
def _compute_is_approval_required(self):
"""Check if the document required approval based on his parents."""
for page in self:
res = page.approval_required
if page.parent_id:
res = res or page.parent_id.is_approval_required
page.is_approval_required = res
@api.multi
@api.depends('approver_gid', 'parent_id.approver_group_ids')
def _compute_approver_group_ids(self):
"""Compute the approver groups based on his parents."""
for page in self:
res = page.approver_gid
if page.parent_id:
res = res | page.parent_id.approver_group_ids
page.approver_group_ids = res
@api.multi
@api.depends('is_approval_required', 'approver_group_ids')
def _compute_am_i_approver(self):
"""Check if the current user can approve changes to this page."""
for rec in self:
rec.am_i_approver = rec.can_user_approve_this_page(self.env.user)
@api.multi
def can_user_approve_this_page(self, user):
"""Check if a user can approve this page."""
self.ensure_one()
# 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:
return False
# and belong to at least one of the approver_groups (if any is set)
if not self.approver_group_ids:
return True
return len(user.groups_id & self.approver_group_ids) > 0
@api.multi
def _compute_has_changes_pending_approval(self):
history = self.env['document.page.history']
for rec in self:
changes = history.search_count([
('page_id', '=', rec.id),
('state', '=', 'to approve')])
rec.has_changes_pending_approval = (changes > 0)
@api.multi
def _create_history(self, vals):
res = super(DocumentPageApproval, self)._create_history(vals)
res.signal_workflow('document_page_auto_confirm')
@api.multi
def action_changes_pending_approval(self):
self.ensure_one()
action = self.env.ref('document_page_approval.action_change_requests')
action = action.read()[0]
context = literal_eval(action['context'])
context['search_default_page_id'] = self.id
context['default_page_id'] = self.id
action['context'] = context
return action

View File

@ -11,117 +11,122 @@ from odoo import api, fields, models
class DocumentPageHistoryWorkflow(models.Model):
"""Useful to manage edition's workflow on a document."""
_inherit = 'document.page.history'
_name = 'document.page.history'
_inherit = ['document.page.history', 'mail.thread']
state = fields.Selection([
('draft', 'Draft'),
('to approve', 'Pending Approval'),
('approved', 'Approved'),
('cancelled', 'Cancelled')],
'Status',
readonly=True,
)
approved_date = fields.Datetime(
'Approved Date',
)
approved_uid = fields.Many2one(
'res.users',
'Approved by',
)
is_approval_required = fields.Boolean(
related='page_id.is_approval_required',
string="Approval required",
)
am_i_owner = fields.Boolean(
compute='_compute_am_i_owner'
)
am_i_approver = fields.Boolean(
related='page_id.am_i_approver'
)
page_url = fields.Text(
compute='_compute_page_url',
string="URL",
)
@api.multi
def page_approval_draft(self):
"""Set a document state as draft and notified the reviewers."""
"""Set a change request as draft"""
self.write({'state': 'draft'})
@api.multi
def page_approval_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')
for page in self:
if page.is_parent_approval_required:
template.send_mail(page.id, force_send=True)
return True
approver_gid = self.env.ref(
'document_page_approval.group_document_approver_user')
for rec in self:
if rec.is_approval_required:
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)
@api.multi
def page_approval_approved(self):
"""Set a document state as approve."""
message_obj = self.env['mail.message']
"""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:
# Trigger computed field update
rec.page_id._compute_history_head()
# Notify state change
rec.message_post(
subtype='mt_comment',
body=_(
'Change request has been approved by %s.'
) % (self.env.user.name)
)
# Notify followers a new version is available
for page_history in self:
subtype = self.env.ref('mail.mt_comment')
message_obj.create(
{'res_id': page_history.page_id.id,
'model': 'document.page',
'subtype_id': subtype.id,
'body': _('New version of the document %s'
' approved.') % page_history.page_id.name
}
)
return True
@api.multi
def _can_user_approve_page(self):
"""Check if a user cas approve the page."""
user = self.env.user
for page in self:
page.can_user_approve_page = page.can_user_approve_this_page(
page.page_id,
user
rec.page_id.message_post(
subtype='mt_comment',
body=_(
'New version of the document %s approved.'
) % (rec.page_id.name)
)
def can_user_approve_this_page(self, page, user):
"""Check if a user can approved the page."""
if page:
res = page.approver_gid in user.groups_id
res = res or self.can_user_approve_this_page(page.parent_id, user)
else:
res = False
return res
@api.multi
def page_approval_cancelled(self):
"""Set a change request as cancelled."""
self.write({'state': 'cancelled'})
for rec in self:
rec.message_post(
subtype='mt_comment',
body=_(
'Change request <b>%s</b> has been cancelled by %s.'
) % (rec.display_name, self.env.user.name)
)
@api.multi
def get_approvers_guids(self):
"""Return the approvers group."""
res = {}
for page in self:
res[page.id] = self.get_approvers_guids_for_page(page.page_id)
return res
def get_approvers_guids_for_page(self, page):
"""Return the approvers group for a page."""
if page:
if page.approver_gid:
res = [page.approver_gid.id]
else:
res = []
res.extend(self.get_approvers_guids_for_page(page.parent_id))
else:
res = []
return res
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 _get_approvers_email(self):
"""Get the approvers email."""
for page in self:
emails = ''
guids = self.get_approvers_guids()
uids = [i.id for i in self.env['res.users'].search([
('groups_id', 'in', guids[page.id])
])]
users = self.env['res.users'].browse(uids)
for user in users:
if user.email:
emails += user.email
emails += ','
else:
empl = self.env['hr.employee'].search([
('login', '=', user.login)
])
if empl.work_email:
emails += empl.work_email
emails += ','
page.get_approvers_email = emails[:-1]
@api.multi
def _get_page_url(self):
"""Get the page url."""
def _compute_page_url(self):
"""Compute the page url."""
for page in self:
base_url = self.env['ir.config_parameter'].get_param(
'web.base.url',
default='http://localhost:8069'
)
page.get_page_url = (
page.page_url = (
'{}/web#db={}&id={}&view_type=form&'
'model=document.page.history').format(
base_url,
@ -129,37 +134,18 @@ class DocumentPageHistoryWorkflow(models.Model):
page.id
)
state = fields.Selection(
[('draft', 'Draft'), ('approved', 'Approved')],
'Status',
readonly=True
)
approved_date = fields.Datetime("Approved Date")
approved_uid = fields.Many2one(
'res.users',
"Approved By"
)
is_parent_approval_required = fields.Boolean(
related='page_id.is_parent_approval_required',
string="parent approval",
store=False
)
can_user_approve_page = fields.Boolean(
compute=_can_user_approve_page,
string="can user approve this page",
store=False
)
get_approvers_email = fields.Text(
compute=_get_approvers_email,
string="get all approvers email",
store=False
)
get_page_url = fields.Text(
compute=_get_page_url,
string="URL",
store=False
)
@api.multi
def _compute_diff(self):
"""Shows a diff between this version and the previous version"""
history = self.env['document.page.history']
for rec in self:
domain = [
('page_id', '=', rec.page_id.id),
('state', '=', 'approved')]
if rec.approved_date:
domain.append(('approved_date', '<', rec.approved_date))
prev = history.search(domain, limit=1, order='approved_date DESC')
if prev:
rec.diff = self.getDiff(prev.id, rec.id)
else:
rec.diff = self.getDiff(False, rec.id)

View File

@ -2,8 +2,13 @@
<odoo>
<record id="group_document_approver_user" model="res.groups">
<field name="name">Document approver</field>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="name">Approver</field>
<field name="category_id" ref="knowledge.module_category_knowledge"/>
<field name="implied_ids" eval="[(4, ref('document_page.group_document_editor'))]"/>
</record>
<record id="document_page.group_document_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_document_approver_user'))]"/>
</record>
</odoo>

View File

@ -1,2 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
document_page_history,document.page.history,model_document_page_history,group_document_approver_user,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
document_page_history document.page.history model_document_page_history group_document_approver_user 1 1 1 0

View File

@ -1,2 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_document_page_approval, test_document_page_history_workflow
from . import test_document_page_approval

View File

@ -1,37 +1,125 @@
# -*- coding: utf-8 -*-
from openerp.tests import common
from odoo.tests import common
class TestDocumentPageApproval(common.TransactionCase):
"""Test document page approval model."""
def test_get_display_content(self):
"""Test page display content."""
# Check content of a category
category = self.env['document.page'].search([
('name', '=', 'OpenERP Features')
])
def setUp(self):
super(TestDocumentPageApproval, self).setUp()
self.page_obj = self.env['document.page']
self.history_obj = self.env['document.page.history']
# demo
self.category1 = self.env.ref('document_page.demo_category1')
self.page1 = self.env.ref('document_page.demo_page1')
self.approver_gid = self.env.ref(
'document_page_approval.group_document_approver_user')
# demo_approval
self.category2 = self.page_obj.create({
'name': 'This category requires approval',
'type': 'category',
'approval_required': True,
'approver_gid': self.approver_gid.id,
})
self.page2 = self.page_obj.create({
'name': 'This page requires approval',
'parent_id': self.category2.id,
'content': 'This content will require approval'
})
self.assertIsNotNone(category.display_content, 'a category')
def test_approval_required(self):
page = self.page2
self.assertTrue(page.is_approval_required)
self.assertTrue(page.has_changes_pending_approval)
self.assertEqual(len(page.history_ids), 0)
# Check content of a page
pages = self.env['document.page'].search([
('parent_id', '=', category.id)
])
def test_change_request_approve(self):
page = self.page2
chreq = self.history_obj.search([
('page_id', '=', page.id),
('state', '!=', 'approved')
])[0]
# It should automatically be in 'to approve' state
self.assertEqual(chreq.state, 'to approve')
self.assertNotEqual(chreq.content, page.content)
# who_am_i
self.assertTrue(chreq.am_i_owner)
self.assertTrue(chreq.am_i_approver)
# approve
chreq.signal_workflow('page_approval_approve')
self.assertEqual(chreq.state, 'approved')
self.assertEqual(chreq.content, page.content)
# new changes should create change requests
page.write({'content': 'New content'})
self.assertNotEqual(page.content, 'New content')
chreq = self.history_obj.search([
('page_id', '=', page.id),
('state', '!=', 'approved')
])[0]
chreq.signal_workflow('page_approval_approve')
self.assertEqual(page.content, 'New content')
def test_change_request_auto_approve(self):
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):
page = self.page2
# aprove everything
self.history_obj.search([
('page_id', '=', page.id),
('state', '!=', 'approved')
]).signal_workflow('page_approval_approve')
# new change request from scrath
chreq = self.history_obj.create({
'page_id': page.id,
'summary': 'Changed something',
'content': 'New content',
})
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.signal_workflow('page_approval_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.signal_workflow('page_approval_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.signal_workflow('page_approval_reopen')
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.signal_workflow('page_approval_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."""
page = self.page2
self.assertTrue(len(page.approver_group_ids) > 0)
def test_get_page_url(self):
"""Test if page url exist."""
pages = self.env['document.page.history'].search([])
page = pages[0]
self.assertIsNotNone(page.display_content, 'Page content')
# Check if approval is required
self.assertTrue(page.is_approval_required(page) ==
category.approval_required)
# Check content of an approval page
page.approval_required = True
self.assertIsNotNone(page.display_content, 'Page content')
# Check if approval is required
self.assertTrue(page.is_approval_required(page))
# Check if parent approval is required
self.assertTrue(page.is_parent_approval_required)
self.assertIsNotNone(page.page_url)

View File

@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
from openerp.tests import common
# Import logger
import logging
# Get the logger
_logger = logging.getLogger(__name__)
class TestDocumentPageHistoryWorkflow(common.TransactionCase):
"""Test document page history workflow."""
def test_can_user_approve_this_page(self):
"""Test if a user can approve this page."""
category = self.env.ref('document_page.demo_category1')
category.approval_required = True
category.approver_gid = self.env.ref(
'document_page_approval.group_document_approver_user')
page = self.env['document.page'].create({
'name': 'Test Page10',
'content': 'A difficult test',
'parent_id': category.id
})
history = self.env['document.page.history'].search(
[
('page_id', '=', page.id)
],
limit=1,
order='create_date DESC'
)
self.assertTrue(history.can_user_approve_page)
def test_get_approvers_guids(self):
"""Get approver guids."""
category = self.env.ref('document_page.demo_category1')
category.approval_required = True
pages = self.env['document.page.history'].search([
('page_id', '=', category.id)
])
page = pages[0]
approvers_guid = page.get_approvers_guids()
self.assertTrue(len(approvers_guid) > 0)
def test_get_approvers_email(self):
"""Get approver email."""
category = self.env.ref('document_page.demo_category1')
category.approval_required = True
pages = self.env['document.page.history'].search([
('page_id', '=', category.id)
])
page = pages[0]
_logger.info("Email: " + str(page.get_approvers_email))
self.assertIsNotNone(page.get_approvers_email)
def test_get_page_url(self):
"""Test if page url exist."""
category = self.env.ref('document_page.demo_category1')
category.approval_required = True
pages = self.env['document.page.history'].search([
('page_id', '=', category.id)
])
page = pages[0]
_logger.info("Page: " + str(page.get_page_url))
self.assertIsNotNone(page.get_page_url)

View File

@ -1,58 +1,110 @@
<?xml version="1.0"?>
<odoo>
<!-- History Form View -->
<record id="wiki_history_form_inherit" model="ir.ui.view">
<field name="name">document.page.history.form</field>
<field name="model">document.page.history</field>
<field name="inherit_id"
ref="document_page.wiki_history_form"/>
<field name="inherit_id" ref="document_page.wiki_history_form"/>
<field name="arch" type="xml">
<xpath expr="//form/label[@for='page_id']"
position="before">
<header attrs="{'invisible':[('is_parent_approval_required','=',False)]}">
<button name="page_approval_approve"
string="Approve"
states="draft"
class="btn-primary"
attrs="{'invisible':[('can_user_approve_page','=',False)]}"/>
<field name="state"
widget="statusbar"
statusbar_visible="draft,approved"/>
<field name="is_parent_approval_required"
invisible="1" />
<field name="can_user_approve_page"
invisible="1" />
<sheet position="before">
<header>
<!-- draft -> to approve -->
<button name="page_approval_to_approve" string="Send to Review" class="oe_highlight"
attrs="{'invisible':['|','|',('is_approval_required','=',False),('am_i_owner','=',False),('state', 'not in', ['draft'])]}"/>
<!-- approve if i am approver -->
<button name="page_approval_approve" string="Approve" class="oe_highlight"
attrs="{'invisible':['|','|',('is_approval_required','=',False),('am_i_approver','=',False),('state','not in',['draft','to approve'])]}"/>
<!-- approve if it's not required and i am owner -->
<button name="page_approval_approve" string="Approve" class="oe_highlight"
attrs="{'invisible':['|','|',('is_approval_required','=',True),('am_i_owner','=',False),('state','not in',['draft', 'to approve'])]}"/>
<!-- cancel if i am owner or approver -->
<button name="page_approval_cancel" string="Cancel"
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['draft','to approve'])]}"/>
<!-- reopen if i am owner or approver -->
<button name="page_approval_reopen" string="Back to draft"
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['cancelled'])]}"/>
<field name="am_i_owner" invisible="1"/>
<field name="am_i_approver" invisible="1"/>
<field name="is_approval_required" invisible="1"/>
<field name="state" widget="statusbar" statusbar_visible="draft,approved" />
</header>
</sheet>
<xpath expr="//field[@name='create_uid']/parent::group" position="after">
<group>
<field name="approved_uid" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/>
<field name="approved_date" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/>
</group>
</xpath>
<field name="content" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute>
</field>
<sheet position="after">
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</sheet>
</field>
</record>
<!-- Page Form View -->
<record id="wiki_form_inherit" model="ir.ui.view">
<field name="name">document.page.form</field>
<field name="model">document.page</field>
<field name="inherit_id"
ref="document_page.view_wiki_form" />
<field name="inherit_id" ref="document_page.view_wiki_form" />
<field name="arch" type="xml">
<field name="display_content"
position="before">
<group class="oe_read_only">
<field name="approved_date" />
<field name="approved_uid" />
</group>
<sheet position="before">
<div class="alert alert-info" role="alert" style="margin-bottom:0px;"
attrs="{'invisible': [('has_changes_pending_approval','=',False)]}">
This document has <b>Changes Pending Approval</b>. You are viewing the last approved content.
</div>
<div class="alert alert-warning oe_edit_only" role="alert" style="margin-bottom:0px;"
attrs="{'invisible': [('is_approval_required','=',False)]}">
This document requires approval. If edited, you will create a new <b>Change Request</b>.
</div>
<field name="is_approval_required" invisible="1"/>
<field name="has_changes_pending_approval" invisible="1"/>
</sheet>
<field name="type" position="before">
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="action_changes_pending_approval"
string="Change Requests" type="object"
attrs="{'invisible':[('has_changes_pending_approval','=',False)]}" icon="fa-edit"/>
</div>
</field>
<field name="content_uid" position="after">
<field name="approved_uid"/>
</field>
<field name="content_date" position="replace">
<field name="approved_date"/>
</field>
<field name="history_ids" position="inside">
<tree>
<field name="id"/>
<field name="approved_date"/>
<field name="summary"/>
<field name="create_uid"/>
<field name="approved_uid"/>
</tree>
</field>
</field>
</record>
<!-- Page Menu Form View -->
<record id="view_wiki_menu_form_inherit" model="ir.ui.view">
<field name="name">document.page.form</field>
<field name="model">document.page</field>
<field name="inherit_id"
ref="document_page.view_wiki_menu_form" />
<field name="inherit_id" ref="document_page.view_wiki_menu_form" />
<field name="arch" type="xml">
<field name="display_content"
position="before">
<group class="oe_read_only"
attrs="{'invisible':[('type','!=','content')]}">
<field name="content" position="before">
<group class="oe_read_only" attrs="{'invisible':[('type','!=','content')]}">
<field name="approved_date" />
<field name="approved_uid" />
</group>
@ -60,11 +112,11 @@
</field>
</record>
<!-- Catgory Form View -->
<record id="view_category_form_inherit" model="ir.ui.view">
<field name="name">document.page.category.form</field>
<field name="model">document.page</field>
<field name="inherit_id"
ref="document_page.view_category_form" />
<field name="inherit_id" ref="document_page.view_category_form" />
<field name="arch" type="xml">
<field name="parent_id" position="after">
<field name="approval_required"/>
@ -74,19 +126,64 @@
</field>
</record>
<!-- History Tree view -->
<record id="view_wiki_history_tree_inherit" model="ir.ui.view">
<field name="name">document.page.history.tree</field>
<field name="model">document.page.history</field>
<field name="inherit_id"
ref="document_page.view_wiki_history_tree" />
<field name="inherit_id" ref="document_page.view_wiki_history_tree"/>
<field name="arch" type="xml">
<field name="page_id" position="after">
<field name="state"
attrs="{'invisible':[('is_parent_approval_required','=',False)]}"/>
<field name="is_parent_approval_required"
invisible="1" />
</field>
<tree position="attributes">
<attribute name="decoration-info">state=='draft'</attribute>
<attribute name="decoration-primary">state=='to approve'</attribute>
<attribute name="decoration-muted">state=='cancelled'</attribute>
</tree>
<tree position="inside">
<field name="state"/>
<field name="approved_uid"/>
<field name="approved_date"/>
</tree>
</field>
</record>
<!-- History Search view -->
<record id="view_wiki_history_filter" model="ir.ui.view">
<field name="name">document.page.history.search</field>
<field name="model">document.page.history</field>
<field name="inherit_id" ref="document_page.view_wiki_history_filter"/>
<field name="arch" type="xml">
<field name="page_id" position="before">
<field name="state"/>
</field>
<field name="create_uid" position="after">
<filter name="draft" string="Draft" domain="[('state','=','draft')]"/>
<filter name="pending" string="Pending Approval" domain="[('state','=','to approve')]"/>
<filter name="approved" string="Approved" domain="[('state','=','approved')]"/>
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancelled')]"/>
</field>
<filter name="group_author" position="before">
<filter name="group_state" string="State" context="{'group_by':'state'}" />
</filter>
<filter name="group_author" position="after">
<filter name="group_approver" string="Approver" context="{'group_by':'approved_uid'}" />
</filter>
</field>
</record>
<!-- Change Requests Action -->
<record model="ir.actions.act_window" id="action_change_requests">
<field name="name">Change Requests</field>
<field name="res_model">document.page.history</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{'search_default_state':'to approve'}</field>
</record>
<menuitem id="menu_page_change_requests"
name="Change Requests"
parent="document_page.menu_wiki"
action="action_change_requests"
sequence="25"
groups="document_page.group_document_editor" />
</odoo>

View File

@ -7,34 +7,98 @@
<field name="on_create">True</field>
</record>
<record model="workflow.activity" id="act_approved">
<field name="wkf_id"
ref="wkf_document_page_history_aproval" />
<field name="name">approved</field>
<field name="kind">function</field>
<field name="action">page_approval_approved()</field>
<field name="flow_stop">True</field>
</record>
<record model="workflow.activity" id="act_draft">
<field name="wkf_id"
ref="wkf_document_page_history_aproval" />
<field name="wkf_id" ref="wkf_document_page_history_aproval" />
<field name="flow_start">True</field>
<field name="name">draft</field>
<field name="kind">function</field>
<field name="action">page_approval_draft()</field>
</record>
<record model="workflow.activity" id="act_to_approve">
<field name="wkf_id" ref="wkf_document_page_history_aproval" />
<field name="name">to approve</field>
<field name="kind">function</field>
<field name="action">page_approval_to_approve()</field>
</record>
<record model="workflow.activity" id="act_approved">
<field name="wkf_id" ref="wkf_document_page_history_aproval" />
<field name="name">approved</field>
<field name="kind">function</field>
<field name="action">page_approval_approved()</field>
<field name="flow_stop">True</field>
</record>
<record model="workflow.activity" id="act_cancelled">
<field name="wkf_id" ref="wkf_document_page_history_aproval" />
<field name="name">cancelled</field>
<field name="kind">function</field>
<field name="action">page_approval_cancelled()</field>
</record>
<!-- Transitions -->
<record model="workflow.transition" id="tdr">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_to_approve"/>
<field name="condition">am_i_owner</field>
<field name="signal">page_approval_to_approve</field>
</record>
<record model="workflow.transition" id="tda">
<field name="act_from" ref="act_draft" />
<field name="act_to" ref="act_approved" />
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_approved"/>
<field name="condition">am_i_approver</field>
<field name="signal">page_approval_approve</field>
</record>
<record model="workflow.transition" id="tra">
<field name="act_from" ref="act_to_approve"/>
<field name="act_to" ref="act_approved"/>
<field name="condition">am_i_approver</field>
<field name="signal">page_approval_approve</field>
</record>
<record model="workflow.transition" id="tad">
<field name="act_from" ref="act_approved" />
<field name="act_to" ref="act_draft" />
<field name="act_from" ref="act_approved"/>
<field name="act_to" ref="act_draft"/>
<field name="condition">am_i_approver</field>
<field name="signal">edit</field>
</record>
<record model="workflow.transition" id="tdc">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_cancelled"/>
<field name="condition">am_i_owner</field>
<field name="signal">page_approval_cancel</field>
</record>
<record model="workflow.transition" id="trc">
<field name="act_from" ref="act_to_approve"/>
<field name="act_to" ref="act_cancelled"/>
<field name="condition">am_i_owner or am_i_approver</field>
<field name="signal">page_approval_cancel</field>
</record>
<record model="workflow.transition" id="tcd">
<field name="act_from" ref="act_cancelled"/>
<field name="act_to" ref="act_draft"/>
<field name="condition">am_i_owner or am_i_approver</field>
<field name="signal">page_approval_reopen</field>
</record>
<!-- Automatic Transitions for change requests created directly from documents -->
<record model="workflow.transition" id="tda_auto">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_approved"/>
<field name="condition">not is_approval_required</field>
<field name="signal">document_page_auto_confirm</field>
</record>
<record model="workflow.transition" id="tdr_auto">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_to_approve"/>
<field name="condition">is_approval_required</field>
<field name="signal">document_page_auto_confirm</field>
</record>
</odoo>