[MIG] document_page_approval: Migration to 13.0

This commit is contained in:
flachica 2020-03-18 14:03:49 +01:00
parent 98f78986b2
commit d729384eb1
11 changed files with 278 additions and 335 deletions

View File

@ -10,3 +10,4 @@ known_odoo=odoo
known_odoo_addons=odoo.addons known_odoo_addons=odoo.addons
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
default_section=THIRDPARTY default_section=THIRDPARTY
known_third_party = setuptools

View File

@ -1,99 +1,68 @@
exclude: | exclude: "^setup/|/static/lib/|/static/src/lib/"
(?x)
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/|
# Repos using Sphinx to generate docs don't need prettying
^docs/_templates/.*\.html$|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)
default_language_version: default_language_version:
python: python3 python: python3
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 19.10b0 rev: 19.3b0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/prettier/prettier - repo: https://github.com/pre-commit/pre-commit-hooks
rev: "1.19.1" rev: v2.3.0
hooks: hooks:
- id: prettier - id: trailing-whitespace
# TODO Avoid awebdeveloper/pre-commit-prettier if possible # exclude autogenerated files
# HACK https://github.com/prettier/prettier/issues/7407 exclude: /README\.rst$|\.pot?$
- repo: https://github.com/awebdeveloper/pre-commit-prettier - id: end-of-file-fixer
rev: v0.0.1 # exclude autogenerated files
hooks: exclude: /README\.rst$|\.pot?$
- id: prettier - id: debug-statements
name: prettier xml plugin - id: flake8
additional_dependencies: name: flake8 except __init__.py
- "prettier@1.19.1" exclude: /__init__\.py$
- "@prettier/plugin-xml@0.7.2" additional_dependencies: ["flake8-bugbear==19.8.0"]
files: \.xml$ - id: flake8
- repo: https://github.com/pre-commit/mirrors-eslint name: flake8 only __init__.py
rev: v6.8.0 args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
hooks: files: /__init__\.py$
- id: eslint additional_dependencies: ["flake8-bugbear==19.8.0"]
verbose: true - id: fix-encoding-pragma
args: args: ["--remove"]
- --color - id: check-case-conflict
- --fix - id: check-docstring-first
- repo: https://github.com/pre-commit/pre-commit-hooks - id: check-executables-have-shebangs
rev: v2.4.0 - id: check-merge-conflict
hooks: - id: check-symlinks
- id: trailing-whitespace - id: check-xml
# exclude autogenerated files - id: mixed-line-ending
exclude: /README\.rst$|\.pot?$ args: ["--fix=lf"]
- id: end-of-file-fixer - repo: https://github.com/pre-commit/mirrors-pylint
# exclude autogenerated files rev: v2.3.1
exclude: /README\.rst$|\.pot?$ hooks:
- id: debug-statements - id: pylint
- id: flake8 name: pylint with optional checks
name: flake8 except __init__.py args: ["--rcfile=.pylintrc", "--exit-zero"]
exclude: /__init__\.py$ verbose: true
additional_dependencies: ["flake8-bugbear==19.8.0"] additional_dependencies: ["pylint-odoo==3.0.3"]
- id: flake8 - id: pylint
name: flake8 only __init__.py name: pylint with mandatory checks
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py args: ["--rcfile=.pylintrc-mandatory"]
files: /__init__\.py$ additional_dependencies: ["pylint-odoo==3.0.3"]
additional_dependencies: ["flake8-bugbear==19.8.0"] - repo: https://github.com/asottile/pyupgrade
- id: fix-encoding-pragma rev: v1.24.0
args: ["--remove"] hooks:
- id: check-case-conflict - id: pyupgrade
- id: check-docstring-first - repo: https://github.com/asottile/seed-isort-config
- id: check-executables-have-shebangs rev: v1.9.3
- id: check-merge-conflict hooks:
# exclude files where underlines are not distinguishable from merge conflicts - id: seed-isort-config
exclude: /README\.rst$|^docs/.*\.rst$ - repo: https://github.com/pre-commit/mirrors-isort
- id: check-symlinks rev: v4.3.21
- id: check-xml hooks:
- id: mixed-line-ending - id: isort
args: ["--fix=lf"] name: isort except __init__.py
- repo: https://github.com/pre-commit/mirrors-pylint exclude: /__init__\.py$
rev: v2.3.1 - repo: https://github.com/pre-commit/mirrors-eslint
hooks: rev: v6.5.1
- id: pylint hooks:
name: pylint with optional checks - id: eslint
args: ["--rcfile=.pylintrc", "--exit-zero"] verbose: true
verbose: true
additional_dependencies: ["pylint-odoo==3.1.0"]
- id: pylint
name: pylint with mandatory checks
args: ["--rcfile=.pylintrc-mandatory"]
additional_dependencies: ["pylint-odoo==3.1.0"]
- repo: https://github.com/asottile/pyupgrade
rev: v1.26.2
hooks:
- id: pyupgrade
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
hooks:
- id: isort
name: isort except __init__.py
exclude: /__init__\.py$
- repo: https://github.com/acsone/setuptools-odoo
rev: 2.5.2
hooks:
- id: setuptools-odoo-make-default

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": "13.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,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

@ -4,7 +4,7 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 12.0\n" "Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n" "Last-Translator: <>\n"
"Language-Team: \n" "Language-Team: \n"
@ -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. \n" 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
msgid "You are not authorized to do this. \n" 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
msgid "You are not authorized to do this. \n" 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,73 @@
# 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.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:
@ -81,8 +77,7 @@ class DocumentPage(models.Model):
res = res or page.parent_id.is_approval_required res = res or page.parent_id.is_approval_required
page.is_approval_required = res page.is_approval_required = res
@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:
@ -91,14 +86,12 @@ class DocumentPage(models.Model):
res = res | page.parent_id.approver_group_ids res = res | page.parent_id.approver_group_ids
page.approver_group_ids = res page.approver_group_ids = res
@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:
rec.am_i_approver = rec.can_user_approve_this_page(self.env.user) rec.am_i_approver = rec.can_user_approve_this_page(self.env.user)
@api.multi
def can_user_approve_this_page(self, user): def can_user_approve_this_page(self, user):
"""Check if a user can approve this page.""" """Check if a user can approve this page."""
self.ensure_one() self.ensure_one()
@ -106,11 +99,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:
@ -118,36 +110,32 @@ class DocumentPage(models.Model):
# to approve, user must belong to any of the approver groups # to approve, user must belong to any of the approver groups
return len(user.groups_id & self.approver_group_ids) > 0 return len(user.groups_id & self.approver_group_ids) > 0
@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
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
def _create_history(self, vals): def _create_history(self, vals):
res = super(DocumentPage, self)._create_history(vals) res = super(DocumentPage, self)._create_history(vals)
res.action_to_approve() res.action_to_approve()
@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,185 +1,166 @@
# 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 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
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
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:
# auto-approve if approval is not required # auto-approve if approval is not required
rec.action_approve() rec.action_approve()
@api.multi
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
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
def action_cancel_and_draft(self): def action_cancel_and_draft(self):
"""Set a change request as draft, cancelling it first""" """Set a change request as draft, cancelling it first"""
self.action_cancel() self.action_cancel()
self.action_draft() self.action_draft()
@api.multi
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
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={}&" "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
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

@ -4,3 +4,7 @@
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com> * Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
* Iván Todorovich <ivan.todorovich@gmail.com> * Iván Todorovich <ivan.todorovich@gmail.com>
* Victor M.M. Torres <victor.martin@tecnativa.com> * Victor M.M. Torres <victor.martin@tecnativa.com>
* `Guadaltech <https://www.guadaltech.es>`_:
* Fernando La Chica <fernando.lachica@guadaltech.es>

View File

@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/knowledge/tree/12.0/document_page_approval"><img alt="OCA/knowledge" src="https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/knowledge-12-0/knowledge-12-0-document_page_approval"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/118/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> <p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/knowledge/tree/13.0/document_page_approval"><img alt="OCA/knowledge" src="https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/knowledge-12-0/knowledge-12-0-document_page_approval"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/118/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module adds a workflow to approve page modifications <p>This module adds a workflow to approve page modifications
and show the approved version by default.</p> and show the approved version by default.</p>
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
@ -411,7 +411,7 @@ page history to review.</li>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/knowledge/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/knowledge/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/knowledge/issues/new?body=module:%20document_page_approval%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/OCA/knowledge/issues/new?body=module:%20document_page_approval%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
@ -440,7 +440,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/knowledge/tree/12.0/document_page_approval">OCA/knowledge</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/knowledge/tree/13.0/document_page_approval">OCA/knowledge</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

View File

@ -2,31 +2,33 @@ 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 +38,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 +52,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 +123,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

@ -196,7 +196,6 @@
<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_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>