[IMP] document_page_approval: black, isort

This commit is contained in:
flachica
2020-07-04 19:11:38 +02:00
committed by FernandoRomera
parent f2ce2fc5ef
commit bc79903d9e
9 changed files with 416 additions and 332 deletions

View File

@@ -2,28 +2,25 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Document Page Approval', "name": "Document Page Approval",
'version': '12.0.1.0.0', "version": "12.0.1.0.0",
"author": "Savoir-faire Linux, Odoo Community Association (OCA)", "author": "Savoir-faire Linux, Odoo Community Association (OCA)",
"website": "http://www.savoirfairelinux.com", "website": "http://www.savoirfairelinux.com",
"license": "AGPL-3", "license": "AGPL-3",
'category': 'Knowledge Management', "category": "Knowledge Management",
'depends': [ "depends": ["document_page", "mail",],
'document_page', "data": [
'mail', "data/email_template.xml",
"views/document_page_approval.xml",
"security/document_page_security.xml",
"security/ir.model.access.csv",
], ],
'data': [ "images": [
'data/email_template.xml', "images/category.png",
'views/document_page_approval.xml', "images/page_history_list.png",
'security/document_page_security.xml', "images/page_history.png",
'security/ir.model.access.csv',
], ],
'images': [ "post_init_hook": "post_init_hook",
'images/category.png', "uninstall_hook": "uninstall_hook",
'images/page_history_list.png', "installable": True,
'images/page_history.png',
],
'post_init_hook': 'post_init_hook',
'uninstall_hook': 'uninstall_hook',
'installable': True,
} }

View File

@@ -1,11 +1,14 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<odoo> <odoo>
<!-- If user wants to make upgrade-proof customizations to email templates, he should edit ir.model.data and check noupdate himself --> <!-- 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"> <record id="email_template_new_draft_need_approval" model="mail.template">
<field name="name">Automated new draft need approval Notification Mail</field> <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
<field name="subject">New version of ${object.display_name} needs your approval</field> name="email_from"
>${object.create_uid.company_id.email or 'noreply@localhost.com'}</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="model_id" ref="model_document_page_history" />
<field name="auto_delete" eval="True" /> <field name="auto_delete" eval="True" />
<field name="lang">${object.create_uid.partner_id.lang}</field> <field name="lang">${object.create_uid.partner_id.lang}</field>
@@ -39,5 +42,4 @@ ${object.diff|safe}
]]> ]]>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -1,25 +1,25 @@
# Copyright 2018 Ivan Todorovich (<ivan.todorovich@gmail.com>) # Copyright 2018 Ivan Todorovich (<ivan.todorovich@gmail.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
def post_init_hook(cr, registry): # pragma: no cover def post_init_hook(cr, registry): # pragma: no cover
# Set all pre-existing pages history to approved # Set all pre-existing pages history to approved
_logger.info('Setting history to approved.') _logger.info("Setting history to approved.")
cr.execute(""" cr.execute(
"""
UPDATE document_page_history UPDATE document_page_history
SET state='approved', SET state='approved',
approved_uid=create_uid, approved_uid=create_uid,
approved_date=create_date approved_date=create_date
WHERE state IS NULL OR state = 'draft' WHERE state IS NULL OR state = 'draft'
""") """
)
def uninstall_hook(cr, registry): # pragma: no cover def uninstall_hook(cr, registry): # pragma: no cover
# Remove unapproved pages # Remove unapproved pages
_logger.info('Deleting unapproved Change Requests.') _logger.info("Deleting unapproved Change Requests.")
cr.execute( cr.execute("DELETE FROM document_page_history " "WHERE state != 'approved'")
"DELETE FROM document_page_history "
"WHERE state != 'approved'"
)

View File

@@ -262,21 +262,24 @@ msgstr ""
#. module: document_page_approval #. module: document_page_approval
#: code:addons/document_page_approval/models/document_page_history.py:102 #: code:addons/document_page_approval/models/document_page_history.py:102
#, python-format #, python-format
msgid "You are not authorized to do this. msgid "You are not authorized to do this.
\n"
"Only approvers with these groups can approve this: " "Only approvers with these groups can approve this: "
msgstr "" msgstr ""
#. module: document_page_approval #. module: document_page_approval
#: code:addons/document_page_approval/models/document_page_history.py:62 #: code:addons/document_page_approval/models/document_page_history.py:62
#, python-format #, python-format
#, python-format msgid "You are not authorized to do this.
\n"
"Only owners or approvers can reopen Change Requests." "Only owners or approvers can reopen Change Requests."
msgstr "" msgstr ""
#. module: document_page_approval #. module: document_page_approval
#: code:addons/document_page_approval/models/document_page_history.py:79 #: code:addons/document_page_approval/models/document_page_history.py:79
#, python-format #, python-format
#: code:addons/document_page_approval/models/document_page_history.py:79 msgid "You are not authorized to do this.
\n"
"Only owners or approvers can request approval." "Only owners or approvers can request approval."
msgstr "" msgstr ""

View File

@@ -2,77 +2,74 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from ast import literal_eval from ast import literal_eval
from odoo import api, fields, models
class DocumentPage(models.Model): class DocumentPage(models.Model):
"""Useful to know the state of a document.""" """Useful to know the state of a document."""
_inherit = 'document.page' _inherit = "document.page"
history_ids = fields.One2many( history_ids = fields.One2many(
order='approved_date DESC', order="approved_date DESC", domain=[("state", "=", "approved")],
domain=[('state', '=', 'approved')],
) )
approved_date = fields.Datetime( approved_date = fields.Datetime(
'Approved Date', "Approved Date",
related='history_head.approved_date', related="history_head.approved_date",
store=True, store=True,
index=True, index=True,
readonly=True, readonly=True,
) )
approved_uid = fields.Many2one( approved_uid = fields.Many2one(
'res.users', "res.users",
'Approved by', "Approved by",
related='history_head.approved_uid', related="history_head.approved_uid",
store=True, store=True,
index=True, index=True,
readonly=True, readonly=True,
) )
approval_required = fields.Boolean( approval_required = fields.Boolean(
'Require approval', "Require approval",
help='Require approval for changes on this page or its child pages.', help="Require approval for changes on this page or its child pages.",
) )
approver_gid = fields.Many2one( approver_gid = fields.Many2one(
"res.groups", "res.groups",
"Approver group", "Approver group",
help='Users must also belong to the Approvers group', help="Users must also belong to the Approvers group",
) )
is_approval_required = fields.Boolean( is_approval_required = fields.Boolean(
'Approval required', "Approval required",
help='If true, changes of this page require approval', help="If true, changes of this page require approval",
compute='_compute_is_approval_required', compute="_compute_is_approval_required",
) )
am_i_approver = fields.Boolean( am_i_approver = fields.Boolean(compute="_compute_am_i_approver")
compute='_compute_am_i_approver'
)
approver_group_ids = fields.Many2many( approver_group_ids = fields.Many2many(
'res.groups', "res.groups",
string='Approver groups', string="Approver groups",
help='Groups that can approve changes to this document', help="Groups that can approve changes to this document",
compute='_compute_approver_group_ids', compute="_compute_approver_group_ids",
) )
has_changes_pending_approval = fields.Boolean( has_changes_pending_approval = fields.Boolean(
compute='_compute_has_changes_pending_approval', compute="_compute_has_changes_pending_approval",
string='Has changes pending approval' string="Has changes pending approval",
) )
user_has_drafts = fields.Boolean( user_has_drafts = fields.Boolean(
compute='_compute_user_has_drafts', compute="_compute_user_has_drafts", string="User has drafts?",
string='User has drafts?',
) )
@api.multi @api.multi
@api.depends('approval_required', 'parent_id.is_approval_required') @api.depends("approval_required", "parent_id.is_approval_required")
def _compute_is_approval_required(self): def _compute_is_approval_required(self):
"""Check if the document required approval based on his parents.""" """Check if the document required approval based on his parents."""
for page in self: for page in self:
@@ -82,7 +79,7 @@ class DocumentPage(models.Model):
page.is_approval_required = res page.is_approval_required = res
@api.multi @api.multi
@api.depends('approver_gid', 'parent_id.approver_group_ids') @api.depends("approver_gid", "parent_id.approver_group_ids")
def _compute_approver_group_ids(self): def _compute_approver_group_ids(self):
"""Compute the approver groups based on his parents.""" """Compute the approver groups based on his parents."""
for page in self: for page in self:
@@ -92,7 +89,7 @@ class DocumentPage(models.Model):
page.approver_group_ids = res page.approver_group_ids = res
@api.multi @api.multi
@api.depends('is_approval_required', 'approver_group_ids') @api.depends("is_approval_required", "approver_group_ids")
def _compute_am_i_approver(self): def _compute_am_i_approver(self):
"""Check if the current user can approve changes to this page.""" """Check if the current user can approve changes to this page."""
for rec in self: for rec in self:
@@ -106,11 +103,10 @@ class DocumentPage(models.Model):
if not self.is_approval_required: if not self.is_approval_required:
return True return True
# if user belongs to 'Knowledge / Manager', he can approve anything # if user belongs to 'Knowledge / Manager', he can approve anything
if user.has_group('document_page.group_document_manager'): if user.has_group("document_page.group_document_manager"):
return True return True
# to approve, user must have approver rights # to approve, user must have approver rights
if not user.has_group( if not user.has_group("document_page_approval.group_document_approver_user"):
'document_page_approval.group_document_approver_user'):
return False return False
# if there aren't any approver_groups_defined, user can approve # if there aren't any approver_groups_defined, user can approve
if not self.approver_group_ids: if not self.approver_group_ids:
@@ -120,21 +116,21 @@ class DocumentPage(models.Model):
@api.multi @api.multi
def _compute_has_changes_pending_approval(self): def _compute_has_changes_pending_approval(self):
history = self.env['document.page.history'] history = self.env["document.page.history"]
for rec in self: for rec in self:
changes = history.search_count([ changes = history.search_count(
('page_id', '=', rec.id), [("page_id", "=", rec.id), ("state", "=", "to approve")]
('state', '=', 'to approve')]) )
rec.has_changes_pending_approval = (changes > 0) rec.has_changes_pending_approval = changes > 0
@api.multi @api.multi
def _compute_user_has_drafts(self): def _compute_user_has_drafts(self):
history = self.env['document.page.history'] history = self.env["document.page.history"]
for rec in self: for rec in self:
changes = history.search_count([ changes = history.search_count(
('page_id', '=', rec.id), [("page_id", "=", rec.id), ("state", "=", "draft")]
('state', '=', 'draft')]) )
rec.user_has_drafts = (changes > 0) rec.user_has_drafts = changes > 0
@api.multi @api.multi
def _create_history(self, vals): def _create_history(self, vals):
@@ -144,10 +140,10 @@ class DocumentPage(models.Model):
@api.multi @api.multi
def action_changes_pending_approval(self): def action_changes_pending_approval(self):
self.ensure_one() self.ensure_one()
action = self.env.ref('document_page_approval.action_change_requests') action = self.env.ref("document_page_approval.action_change_requests")
action = action.read()[0] action = action.read()[0]
context = literal_eval(action['context']) context = literal_eval(action["context"])
context['search_default_page_id'] = self.id context["search_default_page_id"] = self.id
context['default_page_id'] = self.id context["default_page_id"] = self.id
action['context'] = context action["context"] = context
return action return action

View File

@@ -1,90 +1,84 @@
# Copyright (C) 2013 Savoir-faire Linux (<http://www.savoirfairelinux.com>). # Copyright (C) 2013 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tools.translate import _
from odoo import api, fields, models from odoo import api, fields, models
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools.translate import _
class DocumentPageHistory(models.Model): class DocumentPageHistory(models.Model):
"""Useful to manage edition's workflow on a document.""" """Useful to manage edition's workflow on a document."""
_name = 'document.page.history' _name = "document.page.history"
_inherit = ['document.page.history', 'mail.thread'] _inherit = ["document.page.history", "mail.thread"]
state = fields.Selection([ state = fields.Selection(
('draft', 'Draft'), [
('to approve', 'Pending Approval'), ("draft", "Draft"),
('approved', 'Approved'), ("to approve", "Pending Approval"),
('cancelled', 'Cancelled')], ("approved", "Approved"),
'Status', ("cancelled", "Cancelled"),
default='draft', ],
"Status",
default="draft",
readonly=True, readonly=True,
) )
approved_date = fields.Datetime( approved_date = fields.Datetime("Approved Date",)
'Approved Date',
)
approved_uid = fields.Many2one( approved_uid = fields.Many2one("res.users", "Approved by",)
'res.users',
'Approved by',
)
is_approval_required = fields.Boolean( is_approval_required = fields.Boolean(
related='page_id.is_approval_required', related="page_id.is_approval_required", string="Approval required",
string="Approval required",
) )
am_i_owner = fields.Boolean( am_i_owner = fields.Boolean(compute="_compute_am_i_owner")
compute='_compute_am_i_owner'
)
am_i_approver = fields.Boolean( am_i_approver = fields.Boolean(related="page_id.am_i_approver", related_sudo=False,)
related='page_id.am_i_approver',
related_sudo=False,
)
page_url = fields.Text( page_url = fields.Text(compute="_compute_page_url", string="URL",)
compute='_compute_page_url',
string="URL",
)
@api.multi @api.multi
def action_draft(self): def action_draft(self):
"""Set a change request as draft""" """Set a change request as draft"""
for rec in self: for rec in self:
if not rec.state == 'cancelled': if not rec.state == "cancelled":
raise UserError( raise UserError(_("You need to cancel it before reopening."))
_('You need to cancel it before reopening.'))
if not (rec.am_i_owner or rec.am_i_approver): if not (rec.am_i_owner or rec.am_i_approver):
raise UserError( raise UserError(
_('You are not authorized to do this.\r\n' _(
'Only owners or approvers can reopen Change Requests.')) "You are not authorized to do this.\r\n"
rec.write({'state': 'draft'}) "Only owners or approvers can reopen Change Requests."
)
)
rec.write({"state": "draft"})
@api.multi @api.multi
def action_to_approve(self): def action_to_approve(self):
"""Set a change request as to approve""" """Set a change request as to approve"""
template = self.env.ref( template = self.env.ref(
'document_page_approval.email_template_new_draft_need_approval') "document_page_approval.email_template_new_draft_need_approval"
)
approver_gid = self.env.ref( approver_gid = self.env.ref(
'document_page_approval.group_document_approver_user') "document_page_approval.group_document_approver_user"
)
for rec in self: for rec in self:
if rec.state != 'draft': if rec.state != "draft":
raise UserError( raise UserError(_("Can't approve pages in '%s' state.") % rec.state)
_("Can't approve pages in '%s' state.") % rec.state)
if not (rec.am_i_owner or rec.am_i_approver): if not (rec.am_i_owner or rec.am_i_approver):
raise UserError( raise UserError(
_('You are not authorized to do this.\r\n' _(
'Only owners or approvers can request approval.')) "You are not authorized to do this.\r\n"
"Only owners or approvers can request approval."
)
)
# request approval # request approval
if rec.is_approval_required: if rec.is_approval_required:
rec.write({'state': 'to approve'}) rec.write({"state": "to approve"})
guids = [g.id for g in rec.page_id.approver_group_ids] guids = [g.id for g in rec.page_id.approver_group_ids]
users = self.env['res.users'].search([ users = self.env["res.users"].search(
('groups_id', 'in', guids), [("groups_id", "in", guids), ("groups_id", "in", approver_gid.id)]
('groups_id', 'in', approver_gid.id)]) )
rec.message_subscribe([u.id for u in users]) rec.message_subscribe([u.id for u in users])
rec.message_post_with_template(template.id) rec.message_post_with_template(template.id)
else: else:
@@ -95,49 +89,49 @@ class DocumentPageHistory(models.Model):
def action_approve(self): def action_approve(self):
"""Set a change request as approved.""" """Set a change request as approved."""
for rec in self: for rec in self:
if rec.state not in ['draft', 'to approve']: if rec.state not in ["draft", "to approve"]:
raise UserError( raise UserError(_("Can't approve page in '%s' state.") % rec.state)
_("Can't approve page in '%s' state.") % rec.state)
if not rec.am_i_approver: if not rec.am_i_approver:
raise UserError(_( raise UserError(
'You are not authorized to do this.\r\n' _(
'Only approvers with these groups can approve this: ' "You are not authorized to do this.\r\n"
) % ', '.join( "Only approvers with these groups can approve this: "
[g.display_name )
for g in rec.page_id.approver_group_ids])) % ", ".join(
[g.display_name for g in rec.page_id.approver_group_ids]
)
)
# Update state # Update state
rec.write({ rec.write(
'state': 'approved', {
'approved_date': fields.datetime.now(), "state": "approved",
'approved_uid': self.env.uid, "approved_date": fields.datetime.now(),
}) "approved_uid": self.env.uid,
}
)
# Trigger computed field update # Trigger computed field update
rec.page_id._compute_history_head() rec.page_id._compute_history_head()
# Notify state change # Notify state change
rec.message_post( rec.message_post(
subtype='mt_comment', subtype="mt_comment",
body=_( body=_("Change request has been approved by %s.")
'Change request has been approved by %s.' % (self.env.user.name),
) % (self.env.user.name)
) )
# Notify followers a new version is available # Notify followers a new version is available
rec.page_id.message_post( rec.page_id.message_post(
subtype='mt_comment', subtype="mt_comment",
body=_( body=_("New version of the document %s approved.") % (rec.page_id.name),
'New version of the document %s approved.'
) % (rec.page_id.name)
) )
@api.multi @api.multi
def action_cancel(self): def action_cancel(self):
"""Set a change request as cancelled.""" """Set a change request as cancelled."""
self.write({'state': 'cancelled'}) self.write({"state": "cancelled"})
for rec in self: for rec in self:
rec.message_post( rec.message_post(
subtype='mt_comment', subtype="mt_comment",
body=_( body=_("Change request <b>%s</b> has been cancelled by %s.")
'Change request <b>%s</b> has been cancelled by %s.' % (rec.display_name, self.env.user.name),
) % (rec.display_name, self.env.user.name)
) )
@api.multi @api.multi
@@ -150,36 +144,31 @@ class DocumentPageHistory(models.Model):
def _compute_am_i_owner(self): def _compute_am_i_owner(self):
"""Check if current user is the owner""" """Check if current user is the owner"""
for rec in self: for rec in self:
rec.am_i_owner = (rec.create_uid == self.env.user) rec.am_i_owner = rec.create_uid == self.env.user
@api.multi @api.multi
def _compute_page_url(self): def _compute_page_url(self):
"""Compute the page url.""" """Compute the page url."""
for page in self: for page in self:
base_url = self.env['ir.config_parameter'].sudo().get_param( base_url = (
'web.base.url', self.env["ir.config_parameter"]
default='http://localhost:8069' .sudo()
.get_param("web.base.url", default="http://localhost:8069")
) )
page.page_url = ( page.page_url = (
'{}/web#db={}&id={}&view_type=form&' "{}/web#db={}&id={}&view_type=form&" "model=document.page.history"
'model=document.page.history').format( ).format(base_url, self.env.cr.dbname, page.id)
base_url,
self.env.cr.dbname,
page.id
)
@api.multi @api.multi
def _compute_diff(self): def _compute_diff(self):
"""Shows a diff between this version and the previous version""" """Shows a diff between this version and the previous version"""
history = self.env['document.page.history'] history = self.env["document.page.history"]
for rec in self: for rec in self:
domain = [ domain = [("page_id", "=", rec.page_id.id), ("state", "=", "approved")]
('page_id', '=', rec.page_id.id),
('state', '=', 'approved')]
if rec.approved_date: if rec.approved_date:
domain.append(('approved_date', '<', rec.approved_date)) domain.append(("approved_date", "<", rec.approved_date))
prev = history.search(domain, limit=1, order='approved_date DESC') prev = history.search(domain, limit=1, order="approved_date DESC")
if prev: if prev:
rec.diff = self._get_diff(prev.id, rec.id) rec.diff = self._get_diff(prev.id, rec.id)
else: else:

View File

@@ -1,27 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record id="group_document_approver_user" model="res.groups"> <record id="group_document_approver_user" model="res.groups">
<field name="name">Approver</field> <field name="name">Approver</field>
<field name="category_id" ref="knowledge.module_category_knowledge" /> <field name="category_id" ref="knowledge.module_category_knowledge" />
<field name="implied_ids" eval="[(4, ref('document_page.group_document_editor'))]"/> <field
name="implied_ids"
eval="[(4, ref('document_page.group_document_editor'))]"
/>
</record> </record>
<record id="document_page.group_document_manager" model="res.groups"> <record id="document_page.group_document_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('group_document_approver_user'))]" /> <field name="implied_ids" eval="[(4, ref('group_document_approver_user'))]" />
</record> </record>
<record model="ir.rule" id="rule_change_request_global"> <record model="ir.rule" id="rule_change_request_global">
<field name="name">Change Request Global</field> <field name="name">Change Request Global</field>
<field name="model_id" ref="model_document_page_history" /> <field name="model_id" ref="model_document_page_history" />
<field name="groups" eval="[(6, 0, [ref('knowledge.group_document_user')])]" /> <field name="groups" eval="[(6, 0, [ref('knowledge.group_document_user')])]" />
<field name="domain_force">['|',('state','=','approved'),('create_uid','=',user.id)]</field> <field
name="domain_force"
>['|',('state','=','approved'),('create_uid','=',user.id)]</field>
<field name="perm_read" eval="True" /> <field name="perm_read" eval="True" />
<field name="perm_write" eval="True" /> <field name="perm_write" eval="True" />
<field name="perm_unlink" eval="True" /> <field name="perm_unlink" eval="True" />
<field name="perm_create" eval="True" /> <field name="perm_create" eval="True" />
</record> </record>
<record model="ir.rule" id="rule_change_request_approver"> <record model="ir.rule" id="rule_change_request_approver">
<field name="name">Change Request Approver</field> <field name="name">Change Request Approver</field>
<field name="model_id" ref="model_document_page_history" /> <field name="model_id" ref="model_document_page_history" />
@@ -32,5 +33,4 @@
<field name="perm_unlink" eval="True" /> <field name="perm_unlink" eval="True" />
<field name="perm_create" eval="True" /> <field name="perm_create" eval="True" />
</record> </record>
</odoo> </odoo>

View File

@@ -2,31 +2,35 @@ from odoo.tests import common
class TestDocumentPageApproval(common.TransactionCase): class TestDocumentPageApproval(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestDocumentPageApproval, self).setUp() super(TestDocumentPageApproval, self).setUp()
self.page_obj = self.env['document.page'] self.page_obj = self.env["document.page"]
self.history_obj = self.env['document.page.history'] self.history_obj = self.env["document.page.history"]
# demo # demo
self.category1 = self.env.ref('document_page.demo_category1') self.category1 = self.env.ref("document_page.demo_category1")
self.page1 = self.env.ref('document_page.demo_page1') self.page1 = self.env.ref("document_page.demo_page1")
self.approver_gid = self.env.ref( self.approver_gid = self.env.ref(
'document_page_approval.group_document_approver_user') "document_page_approval.group_document_approver_user"
self.env.ref('base.user_root').write({ )
'groups_id': [(4, self.approver_gid.id)], self.env.ref("base.user_root").write(
}) {"groups_id": [(4, self.approver_gid.id)],}
)
# demo_approval # demo_approval
self.category2 = self.page_obj.create({ self.category2 = self.page_obj.create(
'name': 'This category requires approval', {
'type': 'category', "name": "This category requires approval",
'approval_required': True, "type": "category",
'approver_gid': self.approver_gid.id, "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, self.page2 = self.page_obj.create(
'content': 'This content will require approval', {
}) "name": "This page requires approval",
"parent_id": self.category2.id,
"content": "This content will require approval",
}
)
def test_approval_required(self): def test_approval_required(self):
page = self.page2 page = self.page2
@@ -36,13 +40,12 @@ class TestDocumentPageApproval(common.TransactionCase):
def test_change_request_approve(self): def test_change_request_approve(self):
page = self.page2 page = self.page2
chreq = self.history_obj.search([ chreq = self.history_obj.search(
('page_id', '=', page.id), [("page_id", "=", page.id), ("state", "!=", "approved")]
('state', '!=', 'approved') )[0]
])[0]
# It should automatically be in 'to approve' state # It should automatically be in 'to approve' state
self.assertEqual(chreq.state, 'to approve') self.assertEqual(chreq.state, "to approve")
self.assertNotEqual(chreq.content, page.content) self.assertNotEqual(chreq.content, page.content)
# who_am_i # who_am_i
@@ -51,66 +54,66 @@ class TestDocumentPageApproval(common.TransactionCase):
# approve # approve
chreq.action_approve() chreq.action_approve()
self.assertEqual(chreq.state, 'approved') self.assertEqual(chreq.state, "approved")
self.assertEqual(chreq.content, page.content) self.assertEqual(chreq.content, page.content)
# new changes should create change requests # new changes should create change requests
page.write({'content': 'New content'}) page.write({"content": "New content"})
self.assertNotEqual(page.content, 'New content') self.assertNotEqual(page.content, "New content")
chreq = self.history_obj.search([ chreq = self.history_obj.search(
('page_id', '=', page.id), [("page_id", "=", page.id), ("state", "!=", "approved")]
('state', '!=', 'approved') )[0]
])[0]
chreq.action_approve() chreq.action_approve()
self.assertEqual(page.content, 'New content') self.assertEqual(page.content, "New content")
def test_change_request_auto_approve(self): def test_change_request_auto_approve(self):
page = self.page1 page = self.page1
self.assertFalse(page.is_approval_required) self.assertFalse(page.is_approval_required)
page.write({'content': 'New content'}) page.write({"content": "New content"})
self.assertEqual(page.content, 'New content') self.assertEqual(page.content, "New content")
def test_change_request_from_scratch(self): def test_change_request_from_scratch(self):
page = self.page2 page = self.page2
# aprove everything # aprove everything
self.history_obj.search([ self.history_obj.search(
('page_id', '=', page.id), [("page_id", "=", page.id), ("state", "!=", "approved")]
('state', '!=', 'approved') ).action_approve()
]).action_approve()
# new change request from scrath # new change request from scrath
chreq = self.history_obj.create({ chreq = self.history_obj.create(
'page_id': page.id, {
'summary': 'Changed something', "page_id": page.id,
'content': 'New content', "summary": "Changed something",
}) "content": "New content",
}
)
self.assertEqual(chreq.state, 'draft') self.assertEqual(chreq.state, "draft")
self.assertNotEqual(page.content, chreq.content) self.assertNotEqual(page.content, chreq.content)
self.assertNotEqual(page.approved_date, chreq.approved_date) self.assertNotEqual(page.approved_date, chreq.approved_date)
self.assertNotEqual(page.approved_uid, chreq.approved_uid) self.assertNotEqual(page.approved_uid, chreq.approved_uid)
chreq.action_to_approve() chreq.action_to_approve()
self.assertEqual(chreq.state, 'to approve') self.assertEqual(chreq.state, "to approve")
self.assertNotEqual(page.content, chreq.content) self.assertNotEqual(page.content, chreq.content)
self.assertNotEqual(page.approved_date, chreq.approved_date) self.assertNotEqual(page.approved_date, chreq.approved_date)
self.assertNotEqual(page.approved_uid, chreq.approved_uid) self.assertNotEqual(page.approved_uid, chreq.approved_uid)
chreq.action_cancel() chreq.action_cancel()
self.assertEqual(chreq.state, 'cancelled') self.assertEqual(chreq.state, "cancelled")
self.assertNotEqual(page.content, chreq.content) self.assertNotEqual(page.content, chreq.content)
self.assertNotEqual(page.approved_date, chreq.approved_date) self.assertNotEqual(page.approved_date, chreq.approved_date)
self.assertNotEqual(page.approved_uid, chreq.approved_uid) self.assertNotEqual(page.approved_uid, chreq.approved_uid)
chreq.action_draft() chreq.action_draft()
self.assertEqual(chreq.state, 'draft') self.assertEqual(chreq.state, "draft")
self.assertNotEqual(page.content, chreq.content) self.assertNotEqual(page.content, chreq.content)
self.assertNotEqual(page.approved_date, chreq.approved_date) self.assertNotEqual(page.approved_date, chreq.approved_date)
self.assertNotEqual(page.approved_uid, chreq.approved_uid) self.assertNotEqual(page.approved_uid, chreq.approved_uid)
chreq.action_approve() chreq.action_approve()
self.assertEqual(chreq.state, 'approved') self.assertEqual(chreq.state, "approved")
self.assertEqual(page.content, chreq.content) self.assertEqual(page.content, chreq.content)
self.assertEqual(page.approved_date, chreq.approved_date) self.assertEqual(page.approved_date, chreq.approved_date)
self.assertEqual(page.approved_uid, chreq.approved_uid) self.assertEqual(page.approved_uid, chreq.approved_uid)
@@ -122,6 +125,6 @@ class TestDocumentPageApproval(common.TransactionCase):
def test_get_page_url(self): def test_get_page_url(self):
"""Test if page url exist.""" """Test if page url exist."""
pages = self.env['document.page.history'].search([]) pages = self.env["document.page.history"].search([])
page = pages[0] page = pages[0]
self.assertIsNotNone(page.page_url) self.assertIsNotNone(page.page_url)

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<odoo> <odoo>
<!-- History Form View --> <!-- History Form View -->
<record id="wiki_history_form_inherit" model="ir.ui.view"> <record id="wiki_history_form_inherit" model="ir.ui.view">
<field name="name">document.page.history.form</field> <field name="name">document.page.history.form</field>
@@ -10,40 +9,95 @@
<sheet position="before"> <sheet position="before">
<header> <header>
<!-- draft -> to approve --> <!-- draft -> to approve -->
<button name="action_to_approve" type="object" string="Send to Review" class="oe_highlight" <button
attrs="{'invisible':['|','|',('is_approval_required','=',False),('am_i_owner','=',False),('state', 'not in', ['draft'])]}"/> name="action_to_approve"
type="object"
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 --> <!-- approve if i am approver -->
<button name="action_approve" type="object" string="Approve" class="oe_highlight" <button
attrs="{'invisible':['|','|',('is_approval_required','=',False),('am_i_approver','=',False),('state','not in',['draft','to approve'])]}"/> name="action_approve"
type="object"
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 --> <!-- approve if it's not required and i am owner -->
<button name="action_approve" type="object" string="Approve" class="oe_highlight" <button
attrs="{'invisible':['|','|',('is_approval_required','=',True),('am_i_owner','=',False),('state','not in',['draft', 'to approve'])]}"/> name="action_approve"
type="object"
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 --> <!-- cancel if i am owner or approver -->
<button name="action_cancel" type="object" string="Cancel" <button
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['draft','to approve'])]}"/> name="action_cancel"
type="object"
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 --> <!-- reopen if i am owner or approver -->
<button name="action_draft" type="object" string="Back to draft" <button
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['cancelled'])]}"/> name="action_draft"
type="object"
string="Back to draft"
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['cancelled'])]}"
/>
<!-- cancel & reopen, if i am owner or approver --> <!-- cancel & reopen, if i am owner or approver -->
<button name="action_draft" type="object" string="Back to draft" <button
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['to approve'])]}"/> name="action_draft"
type="object"
string="Back to draft"
attrs="{'invisible':['|','&amp;',('am_i_owner','=',False),('am_i_approver','=',False),('state','not in',['to approve'])]}"
/>
<field name="am_i_owner" invisible="1" /> <field name="am_i_owner" invisible="1" />
<field name="am_i_approver" invisible="1" /> <field name="am_i_approver" invisible="1" />
<field name="is_approval_required" invisible="1" /> <field name="is_approval_required" invisible="1" />
<field name="state" widget="statusbar" statusbar_visible="draft,approved" /> <field
name="state"
widget="statusbar"
statusbar_visible="draft,approved"
/>
</header> </header>
</sheet> </sheet>
<xpath expr="//field[@name='create_uid']/parent::group" position="after"> <xpath expr="//field[@name='create_uid']/parent::group" position="after">
<group> <group>
<field name="approved_uid" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/> <field
<field name="approved_date" readonly="1" attrs="{'invisible':[('state','not in',['approved'])]}"/> name="approved_uid"
readonly="1"
attrs="{'invisible':[('state','not in',['approved'])]}"
/>
<field
name="approved_date"
readonly="1"
attrs="{'invisible':[('state','not in',['approved'])]}"
/>
</group> </group>
</xpath> </xpath>
<!-- Readonly fields --> <!-- Readonly fields -->
<field name="content" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> <field name="content" position="attributes">
<field name="page_id" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> <attribute
<field name="name" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> name="attrs"
<field name="summary" position="attributes"><attribute name="attrs">{'readonly': [('state', 'not in', ['draft'])]}</attribute></field> >{'readonly': [('state', 'not in', ['draft'])]}</attribute>
</field>
<field name="page_id" position="attributes">
<attribute
name="attrs"
>{'readonly': [('state', 'not in', ['draft'])]}</attribute>
</field>
<field name="name" position="attributes">
<attribute
name="attrs"
>{'readonly': [('state', 'not in', ['draft'])]}</attribute>
</field>
<field name="summary" position="attributes">
<attribute
name="attrs"
>{'readonly': [('state', 'not in', ['draft'])]}</attribute>
</field>
<!-- Chatter --> <!-- Chatter -->
<sheet position="after"> <sheet position="after">
<div class="oe_chatter"> <div class="oe_chatter">
@@ -53,38 +107,56 @@
</sheet> </sheet>
</field> </field>
</record> </record>
<!-- History Form View Manager Rights --> <!-- History Form View Manager Rights -->
<record id="wiki_history_form_inherit_manager" model="ir.ui.view"> <record id="wiki_history_form_inherit_manager" model="ir.ui.view">
<field name="name">document.page.history.form</field> <field name="name">document.page.history.form</field>
<field name="model">document.page.history</field> <field name="model">document.page.history</field>
<field name="inherit_id" ref="wiki_history_form_inherit" /> <field name="inherit_id" ref="wiki_history_form_inherit" />
<field name="groups_id" eval="[(6, 0, [ref('document_page.group_document_manager')])]"/> <field
name="groups_id"
eval="[(6, 0, [ref('document_page.group_document_manager')])]"
/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Readonly fields --> <!-- Readonly fields -->
<field name="name" position="attributes"><attribute name="attrs">{'readonly': False}</attribute></field> <field name="name" position="attributes">
<field name="summary" position="attributes"><attribute name="attrs">{'readonly': False}</attribute></field> <attribute name="attrs">{'readonly': False}</attribute>
</field>
<field name="summary" position="attributes">
<attribute name="attrs">{'readonly': False}</attribute>
</field>
</field> </field>
</record> </record>
<!-- Page Form View --> <!-- Page Form View -->
<record id="wiki_form_inherit" model="ir.ui.view"> <record id="wiki_form_inherit" model="ir.ui.view">
<field name="name">document.page.form</field> <field name="name">document.page.form</field>
<field name="model">document.page</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="arch" type="xml">
<sheet position="before"> <sheet position="before">
<div class="alert alert-info" role="alert" style="margin-bottom:0px;" <div
attrs="{'invisible': [('has_changes_pending_approval','=',False)]}"> class="alert alert-info"
This document has <b>Changes Pending Approval</b>. You are viewing the last approved content. 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>
<div class="alert alert-warning oe_edit_only" role="alert" style="margin-bottom:0px;" <div
attrs="{'invisible': [('is_approval_required','=',False)]}"> class="alert alert-warning oe_edit_only"
This document requires approval. If edited, you will create a new <b>Change Request</b>. 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> </div>
<div class="alert alert-warning oe_edit_only" role="alert" style="margin-bottom:0px;" <div
attrs="{'invisible': [('user_has_drafts','=',False)]}"> class="alert alert-warning oe_edit_only"
role="alert"
style="margin-bottom:0px;"
attrs="{'invisible': [('user_has_drafts','=',False)]}"
>
<b>You already have a Draft Change Request for this page.</b> <b>You already have a Draft Change Request for this page.</b>
It is highly recommended that you edit that one instead of creating a new one. It is highly recommended that you edit that one instead of creating a new one.
</div> </div>
@@ -92,20 +164,22 @@
<field name="has_changes_pending_approval" invisible="1" /> <field name="has_changes_pending_approval" invisible="1" />
<field name="user_has_drafts" invisible="1" /> <field name="user_has_drafts" invisible="1" />
</sheet> </sheet>
<button name="toggle_active" position="after"> <button name="toggle_active" position="after">
<button class="oe_stat_button" name="action_changes_pending_approval" string="Change Requests" type="object" <button
attrs="{'invisible':[('has_changes_pending_approval','=',False),('user_has_drafts','=',False)]}" icon="fa-edit"/> class="oe_stat_button"
name="action_changes_pending_approval"
string="Change Requests"
type="object"
attrs="{'invisible':[('has_changes_pending_approval','=',False),('user_has_drafts','=',False)]}"
icon="fa-edit"
/>
</button> </button>
<field name="content_uid" position="after"> <field name="content_uid" position="after">
<field name="approved_uid" /> <field name="approved_uid" />
</field> </field>
<field name="content_date" position="replace"> <field name="content_date" position="replace">
<field name="approved_date" /> <field name="approved_date" />
</field> </field>
<field name="history_ids" position="inside"> <field name="history_ids" position="inside">
<tree> <tree>
<field name="id" /> <field name="id" />
@@ -115,10 +189,8 @@
<field name="approved_uid" /> <field name="approved_uid" />
</tree> </tree>
</field> </field>
</field> </field>
</record> </record>
<!-- Page Menu Form View --> <!-- Page Menu Form View -->
<record id="view_wiki_menu_form_inherit" model="ir.ui.view"> <record id="view_wiki_menu_form_inherit" model="ir.ui.view">
<field name="name">document.page.form</field> <field name="name">document.page.form</field>
@@ -126,14 +198,16 @@
<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="arch" type="xml">
<field name="content" position="before"> <field name="content" position="before">
<group class="oe_read_only" attrs="{'invisible':[('type','!=','content')]}"> <group
class="oe_read_only"
attrs="{'invisible':[('type','!=','content')]}"
>
<field name="approved_date" /> <field name="approved_date" />
<field name="approved_uid" /> <field name="approved_uid" />
</group> </group>
</field> </field>
</field> </field>
</record> </record>
<!-- Catgory Form View --> <!-- Catgory Form View -->
<record id="view_category_form_inherit" model="ir.ui.view"> <record id="view_category_form_inherit" model="ir.ui.view">
<field name="name">document.page.category.form</field> <field name="name">document.page.category.form</field>
@@ -142,12 +216,13 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="parent_id" position="after"> <field name="parent_id" position="after">
<field name="approval_required" /> <field name="approval_required" />
<field name="approver_gid" <field
attrs="{'invisible':[('approval_required','!=', True)], 'required':[('approval_required','=', True)]}"/> name="approver_gid"
attrs="{'invisible':[('approval_required','!=', True)], 'required':[('approval_required','=', True)]}"
/>
</field> </field>
</field> </field>
</record> </record>
<!-- History Tree view --> <!-- History Tree view -->
<record id="view_wiki_history_tree_inherit" model="ir.ui.view"> <record id="view_wiki_history_tree_inherit" model="ir.ui.view">
<field name="name">document.page.history.tree</field> <field name="name">document.page.history.tree</field>
@@ -166,8 +241,6 @@
</tree> </tree>
</field> </field>
</record> </record>
<!-- History Search view --> <!-- History Search view -->
<record id="view_wiki_history_filter" model="ir.ui.view"> <record id="view_wiki_history_filter" model="ir.ui.view">
<field name="name">document.page.history.search</field> <field name="name">document.page.history.search</field>
@@ -179,33 +252,54 @@
</field> </field>
<field name="create_uid" position="after"> <field name="create_uid" position="after">
<filter name="draft" string="Draft" domain="[('state','=','draft')]" /> <filter name="draft" string="Draft" domain="[('state','=','draft')]" />
<filter name="pending" string="Pending Approval" domain="[('state','=','to approve')]"/> <filter
<filter name="approved" string="Approved" domain="[('state','=','approved')]"/> name="pending"
<filter name="cancelled" string="Cancelled" domain="[('state','=','cancelled')]"/> string="Pending Approval"
domain="[('state','=','to approve')]"
/>
<filter
name="approved"
string="Approved"
domain="[('state','=','approved')]"
/>
<filter
name="cancelled"
string="Cancelled"
domain="[('state','=','cancelled')]"
/>
</field> </field>
<filter name="group_by_author" position="before"> <filter name="group_by_author" position="before">
<filter name="group_state" string="State" context="{'group_by':'state'}" /> <filter
name="group_state"
string="State"
context="{'group_by':'state'}"
/>
</filter> </filter>
<filter name="group_by_author" position="after"> <filter name="group_by_author" position="after">
<filter name="group_approver" string="Approver" context="{'group_by':'approved_uid'}" /> <filter
name="group_approver"
string="Approver"
context="{'group_by':'approved_uid'}"
/>
</filter> </filter>
</field> </field>
</record> </record>
<!-- Change Requests Action --> <!-- Change Requests Action -->
<record model="ir.actions.act_window" id="action_change_requests"> <record model="ir.actions.act_window" id="action_change_requests">
<field name="name">Change Requests</field> <field name="name">Change Requests</field>
<field name="res_model">document.page.history</field> <field name="res_model">document.page.history</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="context">{'search_default_draft': 1, 'search_default_pending': 1}</field> <field
name="context"
>{'search_default_draft': 1, 'search_default_pending': 1}</field>
</record> </record>
<menuitem
<menuitem id="menu_page_change_requests" id="menu_page_change_requests"
name="Change Requests" name="Change Requests"
parent="document_page.menu_wiki" parent="document_page.menu_wiki"
action="action_change_requests" action="action_change_requests"
sequence="25" sequence="25"
groups="document_page.group_document_editor" /> groups="document_page.group_document_editor"
/>
</odoo> </odoo>