mirror of
https://github.com/OCA/knowledge.git
synced 2025-07-27 10:58:41 -06:00
[MIG] document_page_approval: Migration to 13.0
This commit is contained in:
parent
98f78986b2
commit
d729384eb1
@ -10,3 +10,4 @@ known_odoo=odoo
|
||||
known_odoo_addons=odoo.addons
|
||||
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
|
||||
default_section=THIRDPARTY
|
||||
known_third_party = setuptools
|
||||
|
@ -1,99 +1,68 @@
|
||||
exclude: |
|
||||
(?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.*)
|
||||
exclude: "^setup/|/static/lib/|/static/src/lib/"
|
||||
default_language_version:
|
||||
python: python3
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.10b0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/prettier/prettier
|
||||
rev: "1.19.1"
|
||||
hooks:
|
||||
- id: prettier
|
||||
# TODO Avoid awebdeveloper/pre-commit-prettier if possible
|
||||
# HACK https://github.com/prettier/prettier/issues/7407
|
||||
- repo: https://github.com/awebdeveloper/pre-commit-prettier
|
||||
rev: v0.0.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
name: prettier xml plugin
|
||||
additional_dependencies:
|
||||
- "prettier@1.19.1"
|
||||
- "@prettier/plugin-xml@0.7.2"
|
||||
files: \.xml$
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v6.8.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
verbose: true
|
||||
args:
|
||||
- --color
|
||||
- --fix
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
# exclude autogenerated files
|
||||
exclude: /README\.rst$|\.pot?$
|
||||
- id: end-of-file-fixer
|
||||
# exclude autogenerated files
|
||||
exclude: /README\.rst$|\.pot?$
|
||||
- id: debug-statements
|
||||
- id: flake8
|
||||
name: flake8 except __init__.py
|
||||
exclude: /__init__\.py$
|
||||
additional_dependencies: ["flake8-bugbear==19.8.0"]
|
||||
- id: flake8
|
||||
name: flake8 only __init__.py
|
||||
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
|
||||
files: /__init__\.py$
|
||||
additional_dependencies: ["flake8-bugbear==19.8.0"]
|
||||
- id: fix-encoding-pragma
|
||||
args: ["--remove"]
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-merge-conflict
|
||||
# exclude files where underlines are not distinguishable from merge conflicts
|
||||
exclude: /README\.rst$|^docs/.*\.rst$
|
||||
- id: check-symlinks
|
||||
- id: check-xml
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
- repo: https://github.com/pre-commit/mirrors-pylint
|
||||
rev: v2.3.1
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint with optional checks
|
||||
args: ["--rcfile=.pylintrc", "--exit-zero"]
|
||||
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
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
# exclude autogenerated files
|
||||
exclude: /README\.rst$|\.pot?$
|
||||
- id: end-of-file-fixer
|
||||
# exclude autogenerated files
|
||||
exclude: /README\.rst$|\.pot?$
|
||||
- id: debug-statements
|
||||
- id: flake8
|
||||
name: flake8 except __init__.py
|
||||
exclude: /__init__\.py$
|
||||
additional_dependencies: ["flake8-bugbear==19.8.0"]
|
||||
- id: flake8
|
||||
name: flake8 only __init__.py
|
||||
args: ["--extend-ignore=F401"] # ignore unused imports in __init__.py
|
||||
files: /__init__\.py$
|
||||
additional_dependencies: ["flake8-bugbear==19.8.0"]
|
||||
- id: fix-encoding-pragma
|
||||
args: ["--remove"]
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-xml
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
- repo: https://github.com/pre-commit/mirrors-pylint
|
||||
rev: v2.3.1
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint with optional checks
|
||||
args: ["--rcfile=.pylintrc", "--exit-zero"]
|
||||
verbose: true
|
||||
additional_dependencies: ["pylint-odoo==3.0.3"]
|
||||
- id: pylint
|
||||
name: pylint with mandatory checks
|
||||
args: ["--rcfile=.pylintrc-mandatory"]
|
||||
additional_dependencies: ["pylint-odoo==3.0.3"]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v1.24.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
- repo: https://github.com/asottile/seed-isort-config
|
||||
rev: v1.9.3
|
||||
hooks:
|
||||
- id: seed-isort-config
|
||||
- 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/pre-commit/mirrors-eslint
|
||||
rev: v6.5.1
|
||||
hooks:
|
||||
- id: eslint
|
||||
verbose: true
|
||||
|
@ -2,28 +2,25 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Document Page Approval',
|
||||
'version': '12.0.1.0.0',
|
||||
"name": "Document Page Approval",
|
||||
"version": "13.0.1.0.0",
|
||||
"author": "Savoir-faire Linux, Odoo Community Association (OCA)",
|
||||
"website": "http://www.savoirfairelinux.com",
|
||||
"license": "AGPL-3",
|
||||
'category': 'Knowledge Management',
|
||||
'depends': [
|
||||
'document_page',
|
||||
'mail',
|
||||
"category": "Knowledge Management",
|
||||
"depends": ["document_page", "mail"],
|
||||
"data": [
|
||||
"data/email_template.xml",
|
||||
"views/document_page_approval.xml",
|
||||
"security/document_page_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
],
|
||||
'data': [
|
||||
'data/email_template.xml',
|
||||
'views/document_page_approval.xml',
|
||||
'security/document_page_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
"images": [
|
||||
"images/category.png",
|
||||
"images/page_history_list.png",
|
||||
"images/page_history.png",
|
||||
],
|
||||
'images': [
|
||||
'images/category.png',
|
||||
'images/page_history_list.png',
|
||||
'images/page_history.png',
|
||||
],
|
||||
'post_init_hook': 'post_init_hook',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
'installable': True,
|
||||
"post_init_hook": "post_init_hook",
|
||||
"uninstall_hook": "uninstall_hook",
|
||||
"installable": True,
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
# Copyright 2018 Ivan Todorovich (<ivan.todorovich@gmail.com>)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def post_init_hook(cr, registry): # pragma: no cover
|
||||
# Set all pre-existing pages history to approved
|
||||
_logger.info('Setting history to approved.')
|
||||
cr.execute("""
|
||||
_logger.info("Setting history to approved.")
|
||||
cr.execute(
|
||||
"""
|
||||
UPDATE document_page_history
|
||||
SET state='approved',
|
||||
approved_uid=create_uid,
|
||||
approved_date=create_date
|
||||
WHERE state IS NULL OR state = 'draft'
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry): # pragma: no cover
|
||||
# Remove unapproved pages
|
||||
_logger.info('Deleting unapproved Change Requests.')
|
||||
cr.execute(
|
||||
"DELETE FROM document_page_history "
|
||||
"WHERE state != 'approved'"
|
||||
)
|
||||
_logger.info("Deleting unapproved Change Requests.")
|
||||
cr.execute("DELETE FROM document_page_history " "WHERE state != 'approved'")
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 12.0\n"
|
||||
"Project-Id-Version: Odoo Server 13.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
@ -262,21 +262,24 @@ msgstr ""
|
||||
#. module: document_page_approval
|
||||
#: code:addons/document_page_approval/models/document_page_history.py:102
|
||||
#, 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: "
|
||||
msgstr ""
|
||||
|
||||
#. module: document_page_approval
|
||||
#: code:addons/document_page_approval/models/document_page_history.py:62
|
||||
#, python-format
|
||||
msgid "You are not authorized to do this.
\n"
|
||||
msgid "You are not authorized to do this.
|
||||
\n"
|
||||
"Only owners or approvers can reopen Change Requests."
|
||||
msgstr ""
|
||||
|
||||
#. module: document_page_approval
|
||||
#: code:addons/document_page_approval/models/document_page_history.py:79
|
||||
#, python-format
|
||||
msgid "You are not authorized to do this.
\n"
|
||||
msgid "You are not authorized to do this.
|
||||
\n"
|
||||
"Only owners or approvers can request approval."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2,77 +2,73 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
|
||||
from odoo import api, fields, models
|
||||
from ast import literal_eval
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class DocumentPage(models.Model):
|
||||
"""Useful to know the state of a document."""
|
||||
|
||||
_inherit = 'document.page'
|
||||
_inherit = "document.page"
|
||||
|
||||
history_ids = fields.One2many(
|
||||
order='approved_date DESC',
|
||||
domain=[('state', '=', 'approved')],
|
||||
order="approved_date DESC", domain=[("state", "=", "approved")]
|
||||
)
|
||||
|
||||
approved_date = fields.Datetime(
|
||||
'Approved Date',
|
||||
related='history_head.approved_date',
|
||||
"Approved Date",
|
||||
related="history_head.approved_date",
|
||||
store=True,
|
||||
index=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
approved_uid = fields.Many2one(
|
||||
'res.users',
|
||||
'Approved by',
|
||||
related='history_head.approved_uid',
|
||||
"res.users",
|
||||
"Approved by",
|
||||
related="history_head.approved_uid",
|
||||
store=True,
|
||||
index=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
approval_required = fields.Boolean(
|
||||
'Require approval',
|
||||
help='Require approval for changes on this page or its child pages.',
|
||||
"Require approval",
|
||||
help="Require approval for changes on this page or its child pages.",
|
||||
)
|
||||
|
||||
approver_gid = fields.Many2one(
|
||||
"res.groups",
|
||||
"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(
|
||||
'Approval required',
|
||||
help='If true, changes of this page require approval',
|
||||
compute='_compute_is_approval_required',
|
||||
"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'
|
||||
)
|
||||
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',
|
||||
"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'
|
||||
compute="_compute_has_changes_pending_approval",
|
||||
string="Has changes pending approval",
|
||||
)
|
||||
|
||||
user_has_drafts = fields.Boolean(
|
||||
compute='_compute_user_has_drafts',
|
||||
string='User has drafts?',
|
||||
compute="_compute_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):
|
||||
"""Check if the document required approval based on his parents."""
|
||||
for page in self:
|
||||
@ -81,8 +77,7 @@ class DocumentPage(models.Model):
|
||||
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')
|
||||
@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:
|
||||
@ -91,14 +86,12 @@ class DocumentPage(models.Model):
|
||||
res = res | page.parent_id.approver_group_ids
|
||||
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):
|
||||
"""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()
|
||||
@ -106,11 +99,10 @@ class DocumentPage(models.Model):
|
||||
if not self.is_approval_required:
|
||||
return True
|
||||
# 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
|
||||
# to approve, user must have approver rights
|
||||
if not user.has_group(
|
||||
'document_page_approval.group_document_approver_user'):
|
||||
if not user.has_group("document_page_approval.group_document_approver_user"):
|
||||
return False
|
||||
# if there aren't any approver_groups_defined, user can approve
|
||||
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
|
||||
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']
|
||||
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)
|
||||
changes = history.search_count(
|
||||
[("page_id", "=", rec.id), ("state", "=", "to approve")]
|
||||
)
|
||||
rec.has_changes_pending_approval = changes > 0
|
||||
|
||||
@api.multi
|
||||
def _compute_user_has_drafts(self):
|
||||
history = self.env['document.page.history']
|
||||
history = self.env["document.page.history"]
|
||||
for rec in self:
|
||||
changes = history.search_count([
|
||||
('page_id', '=', rec.id),
|
||||
('state', '=', 'draft')])
|
||||
rec.user_has_drafts = (changes > 0)
|
||||
changes = history.search_count(
|
||||
[("page_id", "=", rec.id), ("state", "=", "draft")]
|
||||
)
|
||||
rec.user_has_drafts = changes > 0
|
||||
|
||||
@api.multi
|
||||
def _create_history(self, vals):
|
||||
res = super(DocumentPage, self)._create_history(vals)
|
||||
res.action_to_approve()
|
||||
|
||||
@api.multi
|
||||
def action_changes_pending_approval(self):
|
||||
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]
|
||||
context = literal_eval(action['context'])
|
||||
context['search_default_page_id'] = self.id
|
||||
context['default_page_id'] = self.id
|
||||
action['context'] = context
|
||||
context = literal_eval(action["context"])
|
||||
context["search_default_page_id"] = self.id
|
||||
context["default_page_id"] = self.id
|
||||
action["context"] = context
|
||||
return action
|
||||
|
@ -1,185 +1,166 @@
|
||||
# Copyright (C) 2013 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
|
||||
# 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 fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
||||
class DocumentPageHistory(models.Model):
|
||||
"""Useful to manage edition's workflow on a document."""
|
||||
|
||||
_name = 'document.page.history'
|
||||
_inherit = ['document.page.history', 'mail.thread']
|
||||
_name = "document.page.history"
|
||||
_inherit = ["document.page.history", "mail.thread"]
|
||||
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
('to approve', 'Pending Approval'),
|
||||
('approved', 'Approved'),
|
||||
('cancelled', 'Cancelled')],
|
||||
'Status',
|
||||
default='draft',
|
||||
state = fields.Selection(
|
||||
[
|
||||
("draft", "Draft"),
|
||||
("to approve", "Pending Approval"),
|
||||
("approved", "Approved"),
|
||||
("cancelled", "Cancelled"),
|
||||
],
|
||||
"Status",
|
||||
default="draft",
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
approved_date = fields.Datetime(
|
||||
'Approved Date',
|
||||
)
|
||||
approved_date = fields.Datetime("Approved Date")
|
||||
|
||||
approved_uid = fields.Many2one(
|
||||
'res.users',
|
||||
'Approved by',
|
||||
)
|
||||
approved_uid = fields.Many2one("res.users", "Approved by")
|
||||
|
||||
is_approval_required = fields.Boolean(
|
||||
related='page_id.is_approval_required',
|
||||
string="Approval required",
|
||||
related="page_id.is_approval_required", string="Approval required"
|
||||
)
|
||||
|
||||
am_i_owner = fields.Boolean(
|
||||
compute='_compute_am_i_owner'
|
||||
)
|
||||
am_i_owner = fields.Boolean(compute="_compute_am_i_owner")
|
||||
|
||||
am_i_approver = fields.Boolean(
|
||||
related='page_id.am_i_approver',
|
||||
related_sudo=False,
|
||||
)
|
||||
am_i_approver = fields.Boolean(related="page_id.am_i_approver", related_sudo=False)
|
||||
|
||||
page_url = fields.Text(
|
||||
compute='_compute_page_url',
|
||||
string="URL",
|
||||
)
|
||||
page_url = fields.Text(compute="_compute_page_url", string="URL")
|
||||
|
||||
@api.multi
|
||||
def action_draft(self):
|
||||
"""Set a change request as draft"""
|
||||
for rec in self:
|
||||
if not rec.state == 'cancelled':
|
||||
raise UserError(
|
||||
_('You need to cancel it before reopening.'))
|
||||
if not rec.state == "cancelled":
|
||||
raise UserError(_("You need to cancel it before reopening."))
|
||||
if not (rec.am_i_owner or rec.am_i_approver):
|
||||
raise UserError(
|
||||
_('You are not authorized to do this.\r\n'
|
||||
'Only owners or approvers can reopen Change Requests.'))
|
||||
rec.write({'state': 'draft'})
|
||||
_(
|
||||
"You are not authorized to do this.\r\n"
|
||||
"Only owners or approvers can reopen Change Requests."
|
||||
)
|
||||
)
|
||||
rec.write({"state": "draft"})
|
||||
|
||||
@api.multi
|
||||
def action_to_approve(self):
|
||||
"""Set a change request as to approve"""
|
||||
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(
|
||||
'document_page_approval.group_document_approver_user')
|
||||
"document_page_approval.group_document_approver_user"
|
||||
)
|
||||
for rec in self:
|
||||
if rec.state != 'draft':
|
||||
raise UserError(
|
||||
_("Can't approve pages in '%s' state.") % rec.state)
|
||||
if rec.state != "draft":
|
||||
raise UserError(_("Can't approve pages in '%s' state.") % rec.state)
|
||||
if not (rec.am_i_owner or rec.am_i_approver):
|
||||
raise UserError(
|
||||
_('You are not authorized to do this.\r\n'
|
||||
'Only owners or approvers can request approval.'))
|
||||
_(
|
||||
"You are not authorized to do this.\r\n"
|
||||
"Only owners or approvers can request approval."
|
||||
)
|
||||
)
|
||||
# request approval
|
||||
if rec.is_approval_required:
|
||||
rec.write({'state': 'to approve'})
|
||||
rec.write({"state": "to approve"})
|
||||
guids = [g.id for g in rec.page_id.approver_group_ids]
|
||||
users = self.env['res.users'].search([
|
||||
('groups_id', 'in', guids),
|
||||
('groups_id', 'in', approver_gid.id)])
|
||||
users = self.env["res.users"].search(
|
||||
[("groups_id", "in", guids), ("groups_id", "in", approver_gid.id)]
|
||||
)
|
||||
rec.message_subscribe([u.id for u in users])
|
||||
rec.message_post_with_template(template.id)
|
||||
else:
|
||||
# auto-approve if approval is not required
|
||||
rec.action_approve()
|
||||
|
||||
@api.multi
|
||||
def action_approve(self):
|
||||
"""Set a change request as approved."""
|
||||
for rec in self:
|
||||
if rec.state not in ['draft', 'to approve']:
|
||||
raise UserError(
|
||||
_("Can't approve page in '%s' state.") % rec.state)
|
||||
if rec.state not in ["draft", "to approve"]:
|
||||
raise UserError(_("Can't approve page in '%s' state.") % rec.state)
|
||||
if not rec.am_i_approver:
|
||||
raise UserError(_(
|
||||
'You are not authorized to do this.\r\n'
|
||||
'Only approvers with these groups can approve this: '
|
||||
) % ', '.join(
|
||||
[g.display_name
|
||||
for g in rec.page_id.approver_group_ids]))
|
||||
raise UserError(
|
||||
_(
|
||||
"You are not authorized to do this.\r\n"
|
||||
"Only approvers with these groups can approve this: "
|
||||
)
|
||||
% ", ".join(
|
||||
[g.display_name for g in rec.page_id.approver_group_ids]
|
||||
)
|
||||
)
|
||||
# Update state
|
||||
rec.write({
|
||||
'state': 'approved',
|
||||
'approved_date': fields.datetime.now(),
|
||||
'approved_uid': self.env.uid,
|
||||
})
|
||||
rec.write(
|
||||
{
|
||||
"state": "approved",
|
||||
"approved_date": fields.datetime.now(),
|
||||
"approved_uid": self.env.uid,
|
||||
}
|
||||
)
|
||||
# Trigger computed field update
|
||||
rec.page_id._compute_history_head()
|
||||
# Notify state change
|
||||
rec.message_post(
|
||||
subtype='mt_comment',
|
||||
body=_(
|
||||
'Change request has been approved by %s.'
|
||||
) % (self.env.user.name)
|
||||
subtype="mt_comment",
|
||||
body=_("Change request has been approved by %s.")
|
||||
% (self.env.user.name),
|
||||
)
|
||||
# Notify followers a new version is available
|
||||
rec.page_id.message_post(
|
||||
subtype='mt_comment',
|
||||
body=_(
|
||||
'New version of the document %s approved.'
|
||||
) % (rec.page_id.name)
|
||||
subtype="mt_comment",
|
||||
body=_("New version of the document %s approved.") % (rec.page_id.name),
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
"""Set a change request as cancelled."""
|
||||
self.write({'state': '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)
|
||||
subtype="mt_comment",
|
||||
body=_("Change request <b>%s</b> has been cancelled by %s.")
|
||||
% (rec.display_name, self.env.user.name),
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def action_cancel_and_draft(self):
|
||||
"""Set a change request as draft, cancelling it first"""
|
||||
self.action_cancel()
|
||||
self.action_draft()
|
||||
|
||||
@api.multi
|
||||
def _compute_am_i_owner(self):
|
||||
"""Check if current user is the owner"""
|
||||
for rec in self:
|
||||
rec.am_i_owner = (rec.create_uid == self.env.user)
|
||||
rec.am_i_owner = rec.create_uid == self.env.user
|
||||
|
||||
@api.multi
|
||||
def _compute_page_url(self):
|
||||
"""Compute the page url."""
|
||||
for page in self:
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'web.base.url',
|
||||
default='http://localhost:8069'
|
||||
base_url = (
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("web.base.url", default="http://localhost:8069")
|
||||
)
|
||||
|
||||
page.page_url = (
|
||||
'{}/web#db={}&id={}&view_type=form&'
|
||||
'model=document.page.history').format(
|
||||
base_url,
|
||||
self.env.cr.dbname,
|
||||
page.id
|
||||
)
|
||||
"{}/web#db={}&id={}&" "model=document.page.history"
|
||||
).format(base_url, self.env.cr.dbname, page.id)
|
||||
|
||||
@api.multi
|
||||
def _compute_diff(self):
|
||||
"""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:
|
||||
domain = [
|
||||
('page_id', '=', rec.page_id.id),
|
||||
('state', '=', 'approved')]
|
||||
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')
|
||||
domain.append(("approved_date", "<", rec.approved_date))
|
||||
prev = history.search(domain, limit=1, order="approved_date DESC")
|
||||
if prev:
|
||||
rec.diff = self._get_diff(prev.id, rec.id)
|
||||
else:
|
||||
|
@ -4,3 +4,7 @@
|
||||
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
|
||||
* Iván Todorovich <ivan.todorovich@gmail.com>
|
||||
* Victor M.M. Torres <victor.martin@tecnativa.com>
|
||||
|
||||
* `Guadaltech <https://www.guadaltech.es>`_:
|
||||
|
||||
* Fernando La Chica <fernando.lachica@guadaltech.es>
|
||||
|
@ -367,7 +367,7 @@ ul.auto-toc {
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! 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
|
||||
and show the approved version by default.</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>.
|
||||
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
|
||||
<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>
|
||||
</div>
|
||||
<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
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,31 +2,33 @@ from odoo.tests import common
|
||||
|
||||
|
||||
class TestDocumentPageApproval(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDocumentPageApproval, self).setUp()
|
||||
self.page_obj = self.env['document.page']
|
||||
self.history_obj = self.env['document.page.history']
|
||||
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.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')
|
||||
self.env.ref('base.user_root').write({
|
||||
'groups_id': [(4, self.approver_gid.id)],
|
||||
})
|
||||
"document_page_approval.group_document_approver_user"
|
||||
)
|
||||
self.env.ref("base.user_root").write({"groups_id": [(4, self.approver_gid.id)]})
|
||||
# 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.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",
|
||||
}
|
||||
)
|
||||
|
||||
def test_approval_required(self):
|
||||
page = self.page2
|
||||
@ -36,13 +38,12 @@ class TestDocumentPageApproval(common.TransactionCase):
|
||||
|
||||
def test_change_request_approve(self):
|
||||
page = self.page2
|
||||
chreq = self.history_obj.search([
|
||||
('page_id', '=', page.id),
|
||||
('state', '!=', 'approved')
|
||||
])[0]
|
||||
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.assertEqual(chreq.state, "to approve")
|
||||
self.assertNotEqual(chreq.content, page.content)
|
||||
|
||||
# who_am_i
|
||||
@ -51,66 +52,66 @@ class TestDocumentPageApproval(common.TransactionCase):
|
||||
|
||||
# approve
|
||||
chreq.action_approve()
|
||||
self.assertEqual(chreq.state, 'approved')
|
||||
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]
|
||||
page.write({"content": "New content"})
|
||||
self.assertNotEqual(page.content, "New content")
|
||||
chreq = self.history_obj.search(
|
||||
[("page_id", "=", page.id), ("state", "!=", "approved")]
|
||||
)[0]
|
||||
chreq.action_approve()
|
||||
self.assertEqual(page.content, 'New content')
|
||||
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')
|
||||
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')
|
||||
]).action_approve()
|
||||
self.history_obj.search(
|
||||
[("page_id", "=", page.id), ("state", "!=", "approved")]
|
||||
).action_approve()
|
||||
|
||||
# new change request from scrath
|
||||
chreq = self.history_obj.create({
|
||||
'page_id': page.id,
|
||||
'summary': 'Changed something',
|
||||
'content': 'New content',
|
||||
})
|
||||
chreq = self.history_obj.create(
|
||||
{
|
||||
"page_id": page.id,
|
||||
"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.approved_date, chreq.approved_date)
|
||||
self.assertNotEqual(page.approved_uid, chreq.approved_uid)
|
||||
|
||||
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.approved_date, chreq.approved_date)
|
||||
self.assertNotEqual(page.approved_uid, chreq.approved_uid)
|
||||
|
||||
chreq.action_cancel()
|
||||
self.assertEqual(chreq.state, 'cancelled')
|
||||
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.action_draft()
|
||||
self.assertEqual(chreq.state, 'draft')
|
||||
self.assertEqual(chreq.state, "draft")
|
||||
self.assertNotEqual(page.content, chreq.content)
|
||||
self.assertNotEqual(page.approved_date, chreq.approved_date)
|
||||
self.assertNotEqual(page.approved_uid, chreq.approved_uid)
|
||||
|
||||
chreq.action_approve()
|
||||
self.assertEqual(chreq.state, 'approved')
|
||||
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)
|
||||
@ -122,6 +123,6 @@ class TestDocumentPageApproval(common.TransactionCase):
|
||||
|
||||
def test_get_page_url(self):
|
||||
"""Test if page url exist."""
|
||||
pages = self.env['document.page.history'].search([])
|
||||
pages = self.env["document.page.history"].search([])
|
||||
page = pages[0]
|
||||
self.assertIsNotNone(page.page_url)
|
||||
|
@ -196,7 +196,6 @@
|
||||
<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_draft': 1, 'search_default_pending': 1}</field>
|
||||
</record>
|
||||
|
Loading…
Reference in New Issue
Block a user