Browse Source

[MIG] base_exception: Migration to 12.0

pull/1557/head
Iván Todorovich 5 years ago
parent
commit
1088f5c3ae
  1. 78
      base_exception/README.rst
  2. 1
      base_exception/__init__.py
  3. 9
      base_exception/__manifest__.py
  4. 129
      base_exception/models/base_exception.py
  5. 8
      base_exception/readme/CONTRIBUTORS.rst
  6. 6
      base_exception/readme/DESCRIPTION.rst
  7. 1
      base_exception/readme/ROADMAP.rst
  8. 437
      base_exception/static/description/index.html
  9. 35
      base_exception/tests/test_base_exception.py
  10. 34
      base_exception/views/base_exception_view.xml
  11. 11
      base_exception/wizard/base_exception_confirm.py
  12. 2
      base_exception/wizard/base_exception_confirm_view.xml

78
base_exception/README.rst

@ -1,11 +1,30 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
============== ==============
Base Exception
Exception Rule
============== ==============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/12.0/base_exception
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-base_exception
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/149/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provide an abstract model to manage customizable This module provide an abstract model to manage customizable
exceptions to be applied on different models (sale order, invoice, ...). exceptions to be applied on different models (sale order, invoice, ...).
@ -13,34 +32,38 @@ It is not useful for itself. You can see an example of implementation
in the 'sale_exception' module. (sale-workflow repository) or in the 'sale_exception' module. (sale-workflow repository) or
'purchase_exception' module (purchase-workflow repository). 'purchase_exception' module (purchase-workflow repository).
Usage
=====
**Table of contents**
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/11.0
.. contents::
:local:
Known issues / Roadmap
======================
Terms used in old api like `pool`, `cr`, `uid` must be removed porting this module in version 12.
Bug Tracker Bug Tracker
=========== ===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
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
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20base_exception%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Roadmap
-------
Terms used in old api like `pool`, `cr`, `uid` must be removed porting this module in version 12.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Images
------
Authors
~~~~~~~
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* Akretion
* Sodexis
* Camptocamp
Contributors Contributors
------------
~~~~~~~~~~~~
* Raphaël Valyi <raphael.valyi@akretion.com> * Raphaël Valyi <raphael.valyi@akretion.com>
* Renato Lima <renato.lima@akretion.com> * Renato Lima <renato.lima@akretion.com>
@ -49,18 +72,21 @@ Contributors
* Yannick Vaucher <yannick.vaucher@camptocamp.com> * Yannick Vaucher <yannick.vaucher@camptocamp.com>
* SodexisTeam <dev@sodexis.com> * SodexisTeam <dev@sodexis.com>
* Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> * Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
* Iván Todorovich <ivan.todorovich@gmail.com>
Maintainer
----------
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png .. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association :alt: Odoo Community Association
:target: https://odoo-community.org :target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose 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. promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/12.0/base_exception>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

1
base_exception/__init__.py

@ -1,2 +1 @@
from . import wizard, models from . import wizard, models

9
base_exception/__manifest__.py

@ -2,18 +2,19 @@
# Copyright 2017 Akretion (http://www.akretion.com) # Copyright 2017 Akretion (http://www.akretion.com)
# Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> # Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{ {
'name': 'Exception Rule', 'name': 'Exception Rule',
'version': '11.0.1.1.0',
'version': '12.0.1.0.0',
'category': 'Generic Modules', 'category': 'Generic Modules',
'summary': """ 'summary': """
This module provide an abstract model to manage customizable This module provide an abstract model to manage customizable
exceptions to be applied on different models (sale order, invoice, ...)""", exceptions to be applied on different models (sale order, invoice, ...)""",
'author': 'author':
"Akretion, Sodexis, Camptocamp, Odoo Community Association (OCA)", "Akretion, Sodexis, Camptocamp, Odoo Community Association (OCA)",
'website': 'http://www.akretion.com',
'depends': ['base_setup'],
'website': 'https://github.com/OCA/server-tools',
'depends': [
'base_setup',
],
'license': 'AGPL-3', 'license': 'AGPL-3',
'data': [ 'data': [
'security/base_exception_security.xml', 'security/base_exception_security.xml',

129
base_exception/models/base_exception.py

@ -6,13 +6,9 @@
import time import time
from functools import wraps from functools import wraps
from odoo import api, fields, models, _ from odoo import api, fields, models, _
import logging
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.tools.safe_eval import safe_eval from odoo.tools.safe_eval import safe_eval
_logger = logging.getLogger(__name__)
def implemented_by_base_exception(func): def implemented_by_base_exception(func):
"""Call a prefixed function based on 'namespace'.""" """Call a prefixed function based on 'namespace'."""
@ -28,50 +24,36 @@ def implemented_by_base_exception(func):
class ExceptionRule(models.Model): class ExceptionRule(models.Model):
_name = 'exception.rule' _name = 'exception.rule'
_description = "Exception Rules"
_description = 'Exception Rule'
_order = 'active desc, sequence asc' _order = 'active desc, sequence asc'
name = fields.Char('Exception Name', required=True, translate=True) name = fields.Char('Exception Name', required=True, translate=True)
description = fields.Text('Description', translate=True) description = fields.Text('Description', translate=True)
sequence = fields.Integer( sequence = fields.Integer(
string='Sequence', string='Sequence',
help="Gives the sequence order when applying the test")
help="Gives the sequence order when applying the test",
)
rule_group = fields.Selection( rule_group = fields.Selection(
selection=[], selection=[],
help="Rule group is used to group the rules that must validated " help="Rule group is used to group the rules that must validated "
"at same time for a target object. Ex: " "at same time for a target object. Ex: "
"validate sale.order.line rules with sale order rules.", "validate sale.order.line rules with sale order rules.",
required=True)
model = fields.Selection(
selection=[],
string='Apply on', required=True)
required=True,
)
model = fields.Selection(selection=[], string='Apply on', required=True)
active = fields.Boolean('Active') active = fields.Boolean('Active')
next_state = fields.Char( next_state = fields.Char(
'Next state', 'Next state',
help="If we detect exception we set the state of object (ex purchase) " help="If we detect exception we set the state of object (ex purchase) "
"to the next_state (ex 'to approve'). If there are more than one " "to the next_state (ex 'to approve'). If there are more than one "
"exception detected and all have a value for next_state, we use" "exception detected and all have a value for next_state, we use"
"the exception having the smallest sequence value")
"the exception having the smallest sequence value",
)
code = fields.Text( code = fields.Text(
'Python Code', 'Python Code',
help="Python code executed to check if the exception apply or " help="Python code executed to check if the exception apply or "
"not. Use failed = True to block the exception", "not. Use failed = True to block the exception",
default="""
# Python code. Use failed = True to block the base.exception.
# You can use the following variables :
# - self: ORM model of the record which is checked
# - "rule_group" or "rule_group_"line:
# browse_record of the base.exception or
# base.exception line (ex rule_group = sale for sale order)
# - object: same as order or line, browse_record of the base.exception or
# base.exception line
# - obj: same as object
# - env: Odoo Environment (i.e. self.env)
# - time: Python time module
# - cr: database cursor
# - uid: current user id
# - context: current context
""")
)
@api.constrains('next_state') @api.constrains('next_state')
def _check_next_state_value(self): def _check_next_state_value(self):
@ -84,60 +66,55 @@ class ExceptionRule(models.Model):
'state']['selection'] 'state']['selection']
select_vals_code = [s[0] for s in select_vals] select_vals_code = [s[0] for s in select_vals]
if rule.next_state not in select_vals_code: if rule.next_state not in select_vals_code:
raise ValidationError(
_('The value "%s" you choose for the "next state" '
'field state of "%s" is wrong.'
' Value must be in this list %s')
% (rule.next_state,
rule.model,
select_vals)
)
raise ValidationError(_(
'The value "%s" you choose for the "next state" '
'field state of "%s" is wrong.'
' Value must be in this list %s'
) % (
rule.next_state,
rule.model,
select_vals
))
class BaseException(models.AbstractModel): class BaseException(models.AbstractModel):
_name = 'base.exception' _name = 'base.exception'
_order = 'main_exception_id asc' _order = 'main_exception_id asc'
_description = 'Exception'
main_exception_id = fields.Many2one( main_exception_id = fields.Many2one(
'exception.rule', 'exception.rule',
compute='_compute_main_error', compute='_compute_main_error',
string='Main Exception', string='Main Exception',
store=True)
rule_group = fields.Selection(
[],
readonly=True,
store=True,
) )
exception_ids = fields.Many2many(
'exception.rule',
string='Exceptions')
rule_group = fields.Selection([], readonly=True)
exception_ids = fields.Many2many('exception.rule', string='Exceptions')
ignore_exception = fields.Boolean('Ignore Exceptions', copy=False) ignore_exception = fields.Boolean('Ignore Exceptions', copy=False)
@api.depends('exception_ids', 'ignore_exception') @api.depends('exception_ids', 'ignore_exception')
def _compute_main_error(self): def _compute_main_error(self):
for obj in self:
if not obj.ignore_exception and obj.exception_ids:
obj.main_exception_id = obj.exception_ids[0]
for rec in self:
if not rec.ignore_exception and rec.exception_ids:
rec.main_exception_id = rec.exception_ids[0]
else: else:
obj.main_exception_id = False
rec.main_exception_id = False
@api.multi @api.multi
def _popup_exceptions(self): def _popup_exceptions(self):
action = self._get_popup_action()
action_data = action.read()[0]
action_data.update({
action = self._get_popup_action().read()[0]
action.update({
'context': { 'context': {
'active_id': self.ids[0], 'active_id': self.ids[0],
'active_ids': self.ids, 'active_ids': self.ids,
'active_model': self._name, 'active_model': self._name,
} }
}) })
return action_data
return action
@api.model @api.model
def _get_popup_action(self): def _get_popup_action(self):
action = self.env.ref('base_exception.action_exception_rule_confirm')
return action
return self.env.ref('base_exception.action_exception_rule_confirm')
@api.multi @api.multi
def _check_exception(self): def _check_exception(self):
@ -191,36 +168,32 @@ class BaseException(models.AbstractModel):
@api.model @api.model
def _exception_rule_eval_context(self, obj_name, rec): def _exception_rule_eval_context(self, obj_name, rec):
return {obj_name: rec,
'self': self.env[rec._name],
'object': rec,
'obj': rec,
'env': self.env,
'cr': self.env.cr,
'uid': self.env.uid,
'user': self.env.user,
'time': time,
# copy context to prevent side-effects of eval
'context': self.env.context.copy()}
return {
'time': time,
'self': rec,
# obj_name, object, obj: deprecated.
# should be removed in future migrations
obj_name: rec,
'object': rec,
'obj': rec,
# copy context to prevent side-effects of eval
# should be deprecated too, accesible through self.
'context': self.env.context.copy()
}
@api.model @api.model
def _rule_eval(self, rule, obj_name, rec): def _rule_eval(self, rule, obj_name, rec):
expr = rule.code
space = self._exception_rule_eval_context(obj_name, rec)
eval_ctx = self._exception_rule_eval_context(obj_name, rec)
try: try:
safe_eval(expr,
space,
mode='exec',
nocopy=True) # nocopy allows to return 'result'
safe_eval(rule.code, eval_ctx, mode='exec', nocopy=True)
except Exception as e: except Exception as e:
raise UserError(
_('Error when evaluating the exception.rule '
'rule:\n %s \n(%s)') % (rule.name, e))
return space.get('failed', False)
raise UserError(_(
'Error when evaluating the exception.rule: '
'%s\n(%s)') % (rule.name, e))
return eval_ctx.get('failed', False)
@api.multi @api.multi
def _detect_exceptions(self, model_exceptions,
sub_exceptions):
def _detect_exceptions(self, model_exceptions, sub_exceptions):
self.ensure_one() self.ensure_one()
exception_ids = [] exception_ids = []
next_state_rule = False next_state_rule = False
@ -228,8 +201,8 @@ class BaseException(models.AbstractModel):
if self._rule_eval(rule, self.rule_group, self): if self._rule_eval(rule, self.rule_group, self):
exception_ids.append(rule.id) exception_ids.append(rule.id)
if rule.next_state: if rule.next_state:
if not next_state_rule or\
rule.sequence < next_state_rule.sequence:
if not next_state_rule or \
rule.sequence < next_state_rule.sequence:
next_state_rule = rule next_state_rule = rule
if sub_exceptions: if sub_exceptions:
for obj_line in self._get_lines(): for obj_line in self._get_lines():

8
base_exception/readme/CONTRIBUTORS.rst

@ -0,0 +1,8 @@
* Raphaël Valyi <raphael.valyi@akretion.com>
* Renato Lima <renato.lima@akretion.com>
* Sébastien BEAU <sebastien.beau@akretion.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* SodexisTeam <dev@sodexis.com>
* Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
* Iván Todorovich <ivan.todorovich@gmail.com>

6
base_exception/readme/DESCRIPTION.rst

@ -0,0 +1,6 @@
This module provide an abstract model to manage customizable
exceptions to be applied on different models (sale order, invoice, ...).
It is not useful for itself. You can see an example of implementation
in the 'sale_exception' module. (sale-workflow repository) or
'purchase_exception' module (purchase-workflow repository).

1
base_exception/readme/ROADMAP.rst

@ -0,0 +1 @@
Terms used in old api like `pool`, `cr`, `uid` must be removed porting this module in version 12.

437
base_exception/static/description/index.html

@ -0,0 +1,437 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Exception Rule</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="exception-rule">
<h1 class="title">Exception Rule</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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/server-tools/tree/12.0/base_exception"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-base_exception"><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/149/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module provide an abstract model to manage customizable
exceptions to be applied on different models (sale order, invoice, …).</p>
<p>It is not useful for itself. You can see an example of implementation
in the ‘sale_exception’ module. (sale-workflow repository) or
‘purchase_exception’ module (purchase-workflow repository).</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#known-issues-roadmap" id="id1">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id1">Known issues / Roadmap</a></h1>
<p>Terms used in old api like <cite>pool</cite>, <cite>cr</cite>, <cite>uid</cite> must be removed porting this module in version 12.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/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/server-tools/issues/new?body=module:%20base_exception%0Aversion:%2012.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">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
<li>Sodexis</li>
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Raphaël Valyi &lt;<a class="reference external" href="mailto:raphael.valyi&#64;akretion.com">raphael.valyi&#64;akretion.com</a>&gt;</li>
<li>Renato Lima &lt;<a class="reference external" href="mailto:renato.lima&#64;akretion.com">renato.lima&#64;akretion.com</a>&gt;</li>
<li>Sébastien BEAU &lt;<a class="reference external" href="mailto:sebastien.beau&#64;akretion.com">sebastien.beau&#64;akretion.com</a>&gt;</li>
<li>Guewen Baconnier &lt;<a class="reference external" href="mailto:guewen.baconnier&#64;camptocamp.com">guewen.baconnier&#64;camptocamp.com</a>&gt;</li>
<li>Yannick Vaucher &lt;<a class="reference external" href="mailto:yannick.vaucher&#64;camptocamp.com">yannick.vaucher&#64;camptocamp.com</a>&gt;</li>
<li>SodexisTeam &lt;<a class="reference external" href="mailto:dev&#64;sodexis.com">dev&#64;sodexis.com</a>&gt;</li>
<li>Mourad EL HADJ MIMOUNE &lt;<a class="reference external" href="mailto:mourad.elhadj.mimoune&#64;akretion.com">mourad.elhadj.mimoune&#64;akretion.com</a>&gt;</li>
<li>Iván Todorovich &lt;<a class="reference external" href="mailto:ivan.todorovich&#64;gmail.com">ivan.todorovich&#64;gmail.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<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/server-tools/tree/12.0/base_exception">OCA/server-tools</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>
</div>
</body>
</html>

35
base_exception/tests/test_base_exception.py

@ -13,8 +13,6 @@ _logger = logging.getLogger(__name__)
@common.post_install(True) @common.post_install(True)
class TestBaseException(common.SavepointCase): class TestBaseException(common.SavepointCase):
# pylint: disable=missing-return
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(TestBaseException, cls).setUpClass() super(TestBaseException, cls).setUpClass()
@ -25,31 +23,28 @@ class TestBaseException(common.SavepointCase):
cls.exception_confirm = cls.env['exception.rule.confirm'] cls.exception_confirm = cls.env['exception.rule.confirm']
cls.exception_rule._fields['rule_group'].selection.append( cls.exception_rule._fields['rule_group'].selection.append(
('test_base', 'test base exception')
)
('test_base', 'Test Base Exception'))
cls.exception_rule._fields['model'].selection.append( cls.exception_rule._fields['model'].selection.append(
('base.exception.test.purchase',
'base.exception.test.purchase')
)
('base.exception.test.purchase', 'Purchase Order'))
cls.exception_rule._fields['model'].selection.append( cls.exception_rule._fields['model'].selection.append(
('base.exception.test.purchase.line',
'base.exception.test.purchase.line')
)
('base.exception.test.purchase.line', 'Purchase Order Line'))
cls.exceptionnozip = cls.env['exception.rule'].create({ cls.exceptionnozip = cls.env['exception.rule'].create({
'name': "No ZIP code on destination", 'name': "No ZIP code on destination",
'sequence': 10, 'sequence': 10,
'rule_group': "test_base", 'rule_group': "test_base",
'model': "base.exception.test.purchase", 'model': "base.exception.test.purchase",
'code': """if not test_base.partner_id.zip:
failed=True""",
'code': "if not test_base.partner_id.zip: failed=True",
}) })
cls.exceptionno_minorder = cls.env['exception.rule'].create({ cls.exceptionno_minorder = cls.env['exception.rule'].create({
'name': "Min order except", 'name': "Min order except",
'sequence': 10, 'sequence': 10,
'rule_group': "test_base", 'rule_group': "test_base",
'model': "base.exception.test.purchase", 'model': "base.exception.test.purchase",
'code': """if test_base.amount_total <= 200.0:
failed=True""",
'code': "if test_base.amount_total <= 200.0: failed=True",
}) })
cls.exceptionno_lineqty = cls.env['exception.rule'].create({ cls.exceptionno_lineqty = cls.env['exception.rule'].create({
@ -57,8 +52,8 @@ class TestBaseException(common.SavepointCase):
'sequence': 10, 'sequence': 10,
'rule_group': "test_base", 'rule_group': "test_base",
'model': "base.exception.test.purchase.line", 'model': "base.exception.test.purchase.line",
'code': """if test_base_line.qty <= 0:
failed=True"""})
'code': "if test_base_line.qty <= 0: failed=True"
})
def test_purchase_order_exception(self): def test_purchase_order_exception(self):
partner = self.env.ref('base.res_partner_1') partner = self.env.ref('base.res_partner_1')
@ -66,9 +61,11 @@ class TestBaseException(common.SavepointCase):
potest1 = self.env['base.exception.test.purchase'].create({ potest1 = self.env['base.exception.test.purchase'].create({
'name': 'Test base exception to basic purchase', 'name': 'Test base exception to basic purchase',
'partner_id': partner.id, 'partner_id': partner.id,
'line_ids': [(0, 0, {'name': "line test",
'amount': 120.0,
'qty': 1.5})],
'line_ids': [(0, 0, {
'name': "line test",
'amount': 120.0,
'qty': 1.5,
})],
}) })
potest1.button_confirm() potest1.button_confirm()

34
base_exception/views/base_exception_view.xml

@ -1,5 +1,6 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<odoo> <odoo>
<record id="view_exception_rule_tree" model="ir.ui.view"> <record id="view_exception_rule_tree" model="ir.ui.view">
<field name="name">exception.rule.tree</field> <field name="name">exception.rule.tree</field>
<field name="model">exception.rule</field> <field name="model">exception.rule</field>
@ -20,24 +21,45 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Exception Rule Setup" name="exception_rule"> <form string="Exception Rule Setup" name="exception_rule">
<sheet> <sheet>
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"/>
</button>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
</div>
<group> <group>
<group> <group>
<field name="name"/>
<field name="description"/> <field name="description"/>
</group> </group>
<group>
<field name="active"/>
<field name="sequence"/>
</group>
</group> </group>
<group> <group>
<group colspan="4" groups="base.group_system"> <group colspan="4" groups="base.group_system">
<field name="rule_group"/> <field name="rule_group"/>
<field name="model"/> <field name="model"/>
<field name="next_state"/> <field name="next_state"/>
<field name="code"/>
</group> </group>
</group> </group>
<notebook>
<page name="code" string="Python Code">
<field name="code" widget="ace" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
</page>
<page name="help" string="Help">
<group>
<div style="margin-top: 4px;">
<h3>Help with Python expressions</h3>
<p>Various fields may use Python code or Python expressions. The following variables can be used:</p>
<ul>
<li><code>self</code>: Record on which the rule is evaluated.</li>
<li><code>time</code>: useful Python libraries</li>
<li>To block the exception use: <code>failed = True</code></li>
</ul>
</div>
</group>
</page>
</notebook>
</sheet> </sheet>
</form> </form>
</field> </field>

11
base_exception/wizard/base_exception_confirm.py

@ -2,19 +2,20 @@
# Copyright 2017 Akretion (http://www.akretion.com) # Copyright 2017 Akretion (http://www.akretion.com)
# Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> # Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
class ExceptionRuleConfirm(models.AbstractModel): class ExceptionRuleConfirm(models.AbstractModel):
_name = 'exception.rule.confirm' _name = 'exception.rule.confirm'
_description = 'Exception Rule Confirm Wizard'
related_model_id = fields.Many2one('base.exception',) related_model_id = fields.Many2one('base.exception',)
exception_ids = fields.Many2many('exception.rule',
string='Exceptions to resolve',
readonly=True)
exception_ids = fields.Many2many(
'exception.rule',
string='Exceptions to resolve',
readonly=True,
)
ignore = fields.Boolean('Ignore Exceptions') ignore = fields.Boolean('Ignore Exceptions')
@api.model @api.model

2
base_exception/wizard/base_exception_confirm_view.xml

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record id="view_exception_rule_confirm" model="ir.ui.view"> <record id="view_exception_rule_confirm" model="ir.ui.view">
<field name="name">Exceptions Rules</field> <field name="name">Exceptions Rules</field>
<field name="model">exception.rule.confirm</field> <field name="model">exception.rule.confirm</field>
@ -34,4 +35,5 @@
<field name="view_id" ref="view_exception_rule_confirm"/> <field name="view_id" ref="view_exception_rule_confirm"/>
<field name="target">new</field> <field name="target">new</field>
</record> </record>
</odoo> </odoo>
Loading…
Cancel
Save