[IMP] base_exception: black, isort
parent
5a4d2aefc0
commit
1100cd5dbc
|
@ -3,24 +3,21 @@
|
||||||
# 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': '12.0.3.0.1',
|
"version": "12.0.3.0.1",
|
||||||
'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": "https://github.com/OCA/server-tools",
|
||||||
'website': 'https://github.com/OCA/server-tools',
|
"depends": ["base_setup"],
|
||||||
'depends': [
|
"license": "AGPL-3",
|
||||||
'base_setup',
|
"data": [
|
||||||
|
"security/base_exception_security.xml",
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"wizard/base_exception_confirm_view.xml",
|
||||||
|
"views/base_exception_view.xml",
|
||||||
],
|
],
|
||||||
'license': 'AGPL-3',
|
"installable": True,
|
||||||
'data': [
|
|
||||||
'security/base_exception_security.xml',
|
|
||||||
'security/ir.model.access.csv',
|
|
||||||
'wizard/base_exception_confirm_view.xml',
|
|
||||||
'views/base_exception_view.xml',
|
|
||||||
],
|
|
||||||
'installable': True,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
from . import base_exception
|
from . import base_exception
|
||||||
|
|
|
@ -3,52 +3,56 @@
|
||||||
# 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).
|
||||||
|
|
||||||
import time
|
|
||||||
import html
|
import html
|
||||||
from odoo import api, fields, models, _
|
import time
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models, osv
|
||||||
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
|
||||||
from odoo import osv
|
|
||||||
|
|
||||||
|
|
||||||
class ExceptionRule(models.Model):
|
class ExceptionRule(models.Model):
|
||||||
_name = 'exception.rule'
|
_name = "exception.rule"
|
||||||
_description = 'Exception Rule'
|
_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",
|
|
||||||
)
|
)
|
||||||
model = fields.Selection(selection=[], string='Apply on', required=True)
|
model = fields.Selection(selection=[], string="Apply on", required=True)
|
||||||
|
|
||||||
exception_type = fields.Selection(
|
exception_type = fields.Selection(
|
||||||
selection=[('by_domain', 'By domain'),
|
selection=[("by_domain", "By domain"), ("by_py_code", "By python code")],
|
||||||
('by_py_code', 'By python code')],
|
string="Exception Type",
|
||||||
string='Exception Type', required=True, default='by_py_code',
|
required=True,
|
||||||
|
default="by_py_code",
|
||||||
help="By python code: allow to define any arbitrary check\n"
|
help="By python code: allow to define any arbitrary check\n"
|
||||||
"By domain: limited to a selection by an odoo domain:\n"
|
"By domain: limited to a selection by an odoo domain:\n"
|
||||||
" performance can be better when exceptions "
|
" performance can be better when exceptions "
|
||||||
" are evaluated with several records")
|
" are evaluated with several records",
|
||||||
domain = fields.Char('Domain')
|
)
|
||||||
|
domain = fields.Char("Domain")
|
||||||
|
|
||||||
active = fields.Boolean('Active', default=True)
|
active = fields.Boolean("Active", default=True)
|
||||||
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",
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.constrains('exception_type', 'domain', 'code')
|
@api.constrains("exception_type", "domain", "code")
|
||||||
def check_exception_type_consistency(self):
|
def check_exception_type_consistency(self):
|
||||||
for rule in self:
|
for rule in self:
|
||||||
if ((rule.exception_type == 'by_py_code' and not rule.code) or
|
if (rule.exception_type == "by_py_code" and not rule.code) or (
|
||||||
(rule.exception_type == 'by_domain' and not rule.domain)):
|
rule.exception_type == "by_domain" and not rule.domain
|
||||||
|
):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("There is a problem of configuration, python code or "
|
_(
|
||||||
"domain is missing to match the exception type.")
|
"There is a problem of configuration, python code or "
|
||||||
|
"domain is missing to match the exception type."
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
|
@ -59,8 +63,8 @@ class ExceptionRule(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class BaseExceptionMethod(models.AbstractModel):
|
class BaseExceptionMethod(models.AbstractModel):
|
||||||
_name = 'base.exception.method'
|
_name = "base.exception.method"
|
||||||
_description = 'Exception Rule Methods'
|
_description = "Exception Rule Methods"
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _get_main_records(self):
|
def _get_main_records(self):
|
||||||
|
@ -81,15 +85,14 @@ class BaseExceptionMethod(models.AbstractModel):
|
||||||
By default, only the rules with the correct model
|
By default, only the rules with the correct model
|
||||||
will be used.
|
will be used.
|
||||||
"""
|
"""
|
||||||
return [('model', '=', self._name)]
|
return [("model", "=", self._name)]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def detect_exceptions(self):
|
def detect_exceptions(self):
|
||||||
"""List all exception_ids applied on self
|
"""List all exception_ids applied on self
|
||||||
Exception ids are also written on records
|
Exception ids are also written on records
|
||||||
"""
|
"""
|
||||||
rules = self.env['exception.rule'].sudo().search(
|
rules = self.env["exception.rule"].sudo().search(self._rule_domain())
|
||||||
self._rule_domain())
|
|
||||||
all_exception_ids = []
|
all_exception_ids = []
|
||||||
rules_to_remove = {}
|
rules_to_remove = {}
|
||||||
rules_to_add = {}
|
rules_to_add = {}
|
||||||
|
@ -123,23 +126,23 @@ class BaseExceptionMethod(models.AbstractModel):
|
||||||
# table
|
# table
|
||||||
# and the "to add" part generates one INSERT (with unnest) per rule.
|
# and the "to add" part generates one INSERT (with unnest) per rule.
|
||||||
for rule_id, records in rules_to_remove.items():
|
for rule_id, records in rules_to_remove.items():
|
||||||
records.write({'exception_ids': [(3, rule_id,)]})
|
records.write({"exception_ids": [(3, rule_id)]})
|
||||||
for rule_id, records in rules_to_add.items():
|
for rule_id, records in rules_to_add.items():
|
||||||
records.write(({'exception_ids': [(4, rule_id,)]}))
|
records.write({"exception_ids": [(4, rule_id)]})
|
||||||
return all_exception_ids
|
return all_exception_ids
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _exception_rule_eval_context(self, rec):
|
def _exception_rule_eval_context(self, rec):
|
||||||
return {
|
return {
|
||||||
'time': time,
|
"time": time,
|
||||||
'self': rec,
|
"self": rec,
|
||||||
# object, obj: deprecated.
|
# object, obj: deprecated.
|
||||||
# should be removed in future migrations
|
# should be removed in future migrations
|
||||||
'object': rec,
|
"object": rec,
|
||||||
'obj': rec,
|
"obj": rec,
|
||||||
# copy context to prevent side-effects of eval
|
# copy context to prevent side-effects of eval
|
||||||
# should be deprecated too, accesible through self.
|
# should be deprecated too, accesible through self.
|
||||||
'context': self.env.context.copy()
|
"context": self.env.context.copy(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
@ -147,26 +150,26 @@ class BaseExceptionMethod(models.AbstractModel):
|
||||||
expr = rule.code
|
expr = rule.code
|
||||||
space = self._exception_rule_eval_context(rec)
|
space = self._exception_rule_eval_context(rec)
|
||||||
try:
|
try:
|
||||||
safe_eval(expr,
|
safe_eval(
|
||||||
space,
|
expr, space, mode="exec", nocopy=True
|
||||||
mode='exec',
|
) # nocopy allows to return 'result'
|
||||||
nocopy=True) # nocopy allows to return 'result'
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_('Error when evaluating the exception.rule '
|
_("Error when evaluating the exception.rule " "rule:\n %s \n(%s)")
|
||||||
'rule:\n %s \n(%s)') % (rule.name, e))
|
% (rule.name, e)
|
||||||
return space.get('failed', False)
|
)
|
||||||
|
return space.get("failed", False)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _detect_exceptions(self, rule):
|
def _detect_exceptions(self, rule):
|
||||||
if rule.exception_type == 'by_py_code':
|
if rule.exception_type == "by_py_code":
|
||||||
return self._detect_exceptions_by_py_code(rule)
|
return self._detect_exceptions_by_py_code(rule)
|
||||||
elif rule.exception_type == 'by_domain':
|
elif rule.exception_type == "by_domain":
|
||||||
return self._detect_exceptions_by_domain(rule)
|
return self._detect_exceptions_by_domain(rule)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _get_base_domain(self):
|
def _get_base_domain(self):
|
||||||
return [('ignore_exception', '=', False), ('id', 'in', self.ids)]
|
return [("ignore_exception", "=", False), ("id", "in", self.ids)]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _detect_exceptions_by_py_code(self, rule):
|
def _detect_exceptions_by_py_code(self, rule):
|
||||||
|
@ -193,34 +196,29 @@ class BaseExceptionMethod(models.AbstractModel):
|
||||||
|
|
||||||
|
|
||||||
class BaseException(models.AbstractModel):
|
class BaseException(models.AbstractModel):
|
||||||
_inherit = 'base.exception.method'
|
_inherit = "base.exception.method"
|
||||||
_name = 'base.exception'
|
_name = "base.exception"
|
||||||
_order = 'main_exception_id asc'
|
_order = "main_exception_id asc"
|
||||||
_description = 'Exception'
|
_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,
|
store=True,
|
||||||
)
|
)
|
||||||
exceptions_summary = fields.Html(
|
exceptions_summary = fields.Html(
|
||||||
'Exceptions Summary',
|
"Exceptions Summary", compute="_compute_exceptions_summary"
|
||||||
compute='_compute_exceptions_summary',
|
|
||||||
)
|
)
|
||||||
exception_ids = fields.Many2many(
|
exception_ids = fields.Many2many("exception.rule", string="Exceptions", copy=False)
|
||||||
'exception.rule',
|
ignore_exception = fields.Boolean("Ignore Exceptions", copy=False)
|
||||||
string='Exceptions',
|
|
||||||
copy=False,
|
|
||||||
)
|
|
||||||
ignore_exception = fields.Boolean('Ignore Exceptions', copy=False)
|
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def action_ignore_exceptions(self):
|
def action_ignore_exceptions(self):
|
||||||
self.write({'ignore_exception': True})
|
self.write({"ignore_exception": True})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.depends('exception_ids', 'ignore_exception')
|
@api.depends("exception_ids", "ignore_exception")
|
||||||
def _compute_main_error(self):
|
def _compute_main_error(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if not rec.ignore_exception and rec.exception_ids:
|
if not rec.ignore_exception and rec.exception_ids:
|
||||||
|
@ -228,29 +226,35 @@ class BaseException(models.AbstractModel):
|
||||||
else:
|
else:
|
||||||
rec.main_exception_id = False
|
rec.main_exception_id = False
|
||||||
|
|
||||||
@api.depends('exception_ids', 'ignore_exception')
|
@api.depends("exception_ids", "ignore_exception")
|
||||||
def _compute_exceptions_summary(self):
|
def _compute_exceptions_summary(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.exception_ids and not rec.ignore_exception:
|
if rec.exception_ids and not rec.ignore_exception:
|
||||||
rec.exceptions_summary = '<ul>%s</ul>' % ''.join([
|
rec.exceptions_summary = "<ul>%s</ul>" % "".join(
|
||||||
'<li>%s: <i>%s</i></li>' % tuple(map(html.escape, (
|
[
|
||||||
e.name, e.description))) for e in rec.exception_ids])
|
"<li>%s: <i>%s</i></li>"
|
||||||
|
% tuple(map(html.escape, (e.name, e.description)))
|
||||||
|
for e in rec.exception_ids
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _popup_exceptions(self):
|
def _popup_exceptions(self):
|
||||||
action = self._get_popup_action().read()[0]
|
action = self._get_popup_action().read()[0]
|
||||||
action.update({
|
action.update(
|
||||||
'context': {
|
{
|
||||||
'active_id': self.ids[0],
|
"context": {
|
||||||
'active_ids': self.ids,
|
"active_id": self.ids[0],
|
||||||
'active_model': self._name,
|
"active_ids": self.ids,
|
||||||
|
"active_model": self._name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_popup_action(self):
|
def _get_popup_action(self):
|
||||||
return self.env.ref('base_exception.action_exception_rule_confirm')
|
return self.env.ref("base_exception.action_exception_rule_confirm")
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _check_exception(self):
|
def _check_exception(self):
|
||||||
|
@ -266,5 +270,5 @@ class BaseException(models.AbstractModel):
|
||||||
"""
|
"""
|
||||||
exception_ids = self.detect_exceptions()
|
exception_ids = self.detect_exceptions()
|
||||||
if exception_ids:
|
if exception_ids:
|
||||||
exceptions = self.env['exception.rule'].browse(exception_ids)
|
exceptions = self.env["exception.rule"].browse(exception_ids)
|
||||||
raise ValidationError('\n'.join(exceptions.mapped('name')))
|
raise ValidationError("\n".join(exceptions.mapped("name")))
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
from . import common
|
from . import common
|
||||||
from . import purchase_test
|
from . import purchase_test
|
||||||
from . import test_base_exception
|
from . import test_base_exception
|
||||||
|
|
|
@ -8,6 +8,7 @@ def setup_test_model(env, model_clses):
|
||||||
|
|
||||||
env.registry.setup_models(env.cr)
|
env.registry.setup_models(env.cr)
|
||||||
env.registry.init_models(
|
env.registry.init_models(
|
||||||
env.cr, [model_cls._name for model_cls in model_clses],
|
env.cr,
|
||||||
dict(env.context, update_custom_fields=True)
|
[model_cls._name for model_cls in model_clses],
|
||||||
|
dict(env.context, update_custom_fields=True),
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,58 +4,63 @@ from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class PurchaseTest(models.Model):
|
class PurchaseTest(models.Model):
|
||||||
_inherit = 'base.exception'
|
_inherit = "base.exception"
|
||||||
_name = "base.exception.test.purchase"
|
_name = "base.exception.test.purchase"
|
||||||
_description = "Base Ecxeption Test Model"
|
_description = "Base Ecxeption Test Model"
|
||||||
|
|
||||||
name = fields.Char(required=True)
|
name = fields.Char(required=True)
|
||||||
user_id = fields.Many2one('res.users', string='Responsible')
|
user_id = fields.Many2one("res.users", string="Responsible")
|
||||||
state = fields.Selection(
|
state = fields.Selection(
|
||||||
[('draft', 'New'), ('cancel', 'Cancelled'),
|
[
|
||||||
('purchase', 'Purchase'),
|
("draft", "New"),
|
||||||
('to approve', 'To approve'), ('done', 'Done')],
|
("cancel", "Cancelled"),
|
||||||
string="Status", readonly=True, default='draft')
|
("purchase", "Purchase"),
|
||||||
|
("to approve", "To approve"),
|
||||||
|
("done", "Done"),
|
||||||
|
],
|
||||||
|
string="Status",
|
||||||
|
readonly=True,
|
||||||
|
default="draft",
|
||||||
|
)
|
||||||
active = fields.Boolean(default=True)
|
active = fields.Boolean(default=True)
|
||||||
partner_id = fields.Many2one('res.partner', string='Partner')
|
partner_id = fields.Many2one("res.partner", string="Partner")
|
||||||
line_ids = fields.One2many(
|
line_ids = fields.One2many("base.exception.test.purchase.line", "lead_id")
|
||||||
'base.exception.test.purchase.line', 'lead_id')
|
amount_total = fields.Float(compute="_compute_amount_total", store=True)
|
||||||
amount_total = fields.Float(
|
|
||||||
compute='_compute_amount_total', store=True)
|
|
||||||
|
|
||||||
@api.depends('line_ids')
|
@api.depends("line_ids")
|
||||||
def _compute_amount_total(cls):
|
def _compute_amount_total(cls):
|
||||||
for record in cls:
|
for record in cls:
|
||||||
for line in record.line_ids:
|
for line in record.line_ids:
|
||||||
record.amount_total += line.amount * line.qty
|
record.amount_total += line.amount * line.qty
|
||||||
|
|
||||||
@api.constrains('ignore_exception', 'line_ids', 'state')
|
@api.constrains("ignore_exception", "line_ids", "state")
|
||||||
def test_purchase_check_exception(cls):
|
def test_purchase_check_exception(cls):
|
||||||
orders = cls.filtered(lambda s: s.state == 'purchase')
|
orders = cls.filtered(lambda s: s.state == "purchase")
|
||||||
if orders:
|
if orders:
|
||||||
orders._check_exception()
|
orders._check_exception()
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def button_approve(cls, force=False):
|
def button_approve(cls, force=False):
|
||||||
cls.write({'state': 'to approve'})
|
cls.write({"state": "to approve"})
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def button_draft(cls):
|
def button_draft(cls):
|
||||||
cls.write({'state': 'draft'})
|
cls.write({"state": "draft"})
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def button_confirm(cls):
|
def button_confirm(cls):
|
||||||
cls.write({'state': 'purchase'})
|
cls.write({"state": "purchase"})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def button_cancel(cls):
|
def button_cancel(cls):
|
||||||
cls.write({'state': 'cancel'})
|
cls.write({"state": "cancel"})
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _reverse_field(self):
|
def _reverse_field(self):
|
||||||
return 'test_purchase_ids'
|
return "test_purchase_ids"
|
||||||
|
|
||||||
|
|
||||||
class LineTest(models.Model):
|
class LineTest(models.Model):
|
||||||
|
@ -63,7 +68,6 @@ class LineTest(models.Model):
|
||||||
_description = "Base Exception Test Model Line"
|
_description = "Base Exception Test Model Line"
|
||||||
|
|
||||||
name = fields.Char()
|
name = fields.Char()
|
||||||
lead_id = fields.Many2one('base.exception.test.purchase',
|
lead_id = fields.Many2one("base.exception.test.purchase", ondelete="cascade")
|
||||||
ondelete='cascade')
|
|
||||||
qty = fields.Float()
|
qty = fields.Float()
|
||||||
amount = fields.Float()
|
amount = fields.Float()
|
||||||
|
|
|
@ -1,70 +1,79 @@
|
||||||
# Copyright 2016 Akretion Mourad EL HADJ MIMOUNE
|
# Copyright 2016 Akretion Mourad EL HADJ MIMOUNE
|
||||||
# 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.tests import common
|
|
||||||
from odoo.exceptions import ValidationError
|
|
||||||
from odoo import fields
|
|
||||||
from .common import setup_test_model
|
|
||||||
from .purchase_test import PurchaseTest, LineTest
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from odoo import fields
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tests import common
|
||||||
|
|
||||||
|
from .common import setup_test_model
|
||||||
|
from .purchase_test import LineTest, PurchaseTest
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@common.at_install(False)
|
@common.at_install(False)
|
||||||
@common.post_install(True)
|
@common.post_install(True)
|
||||||
class TestBaseException(common.SavepointCase):
|
class TestBaseException(common.SavepointCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super(TestBaseException, cls).setUpClass()
|
super(TestBaseException, cls).setUpClass()
|
||||||
setup_test_model(cls.env, [PurchaseTest, LineTest])
|
setup_test_model(cls.env, [PurchaseTest, LineTest])
|
||||||
|
|
||||||
cls.base_exception = cls.env['base.exception']
|
cls.base_exception = cls.env["base.exception"]
|
||||||
cls.exception_rule = cls.env['exception.rule']
|
cls.exception_rule = cls.env["exception.rule"]
|
||||||
if 'test_purchase_ids' not in cls.exception_rule._fields:
|
if "test_purchase_ids" not in cls.exception_rule._fields:
|
||||||
field = fields.Many2many('base.exception.test.purchase')
|
field = fields.Many2many("base.exception.test.purchase")
|
||||||
cls.exception_rule._add_field('test_purchase_ids', field)
|
cls.exception_rule._add_field("test_purchase_ids", field)
|
||||||
cls.exception_confirm = cls.env['exception.rule.confirm']
|
cls.exception_confirm = cls.env["exception.rule.confirm"]
|
||||||
cls.exception_rule._fields['model'].selection.append(
|
cls.exception_rule._fields["model"].selection.append(
|
||||||
('base.exception.test.purchase', 'Purchase Order'))
|
("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', 'Purchase Order 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",
|
{
|
||||||
'sequence': 10,
|
"name": "No ZIP code on destination",
|
||||||
'model': "base.exception.test.purchase",
|
"sequence": 10,
|
||||||
'code': "if not obj.partner_id.zip: failed=True",
|
"model": "base.exception.test.purchase",
|
||||||
})
|
"code": "if not obj.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",
|
{
|
||||||
'sequence': 10,
|
"name": "Min order except",
|
||||||
'model': "base.exception.test.purchase",
|
"sequence": 10,
|
||||||
'code': "if obj.amount_total <= 200.0: failed=True",
|
"model": "base.exception.test.purchase",
|
||||||
})
|
"code": "if obj.amount_total <= 200.0: failed=True",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
cls.exceptionno_lineqty = cls.env['exception.rule'].create({
|
cls.exceptionno_lineqty = cls.env["exception.rule"].create(
|
||||||
'name': "Qty > 0",
|
{
|
||||||
'sequence': 10,
|
"name": "Qty > 0",
|
||||||
'model': "base.exception.test.purchase.line",
|
"sequence": 10,
|
||||||
'code': "if obj.qty <= 0: failed=True"
|
"model": "base.exception.test.purchase.line",
|
||||||
})
|
"code": "if obj.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")
|
||||||
partner.zip = False
|
partner.zip = False
|
||||||
potest1 = self.env['base.exception.test.purchase'].create({
|
potest1 = self.env["base.exception.test.purchase"].create(
|
||||||
'name': 'Test base exception to basic purchase',
|
{
|
||||||
'partner_id': partner.id,
|
"name": "Test base exception to basic purchase",
|
||||||
'line_ids': [(0, 0, {
|
"partner_id": partner.id,
|
||||||
'name': "line test",
|
"line_ids": [
|
||||||
'amount': 120.0,
|
(0, 0, {"name": "line test", "amount": 120.0, "qty": 1.5})
|
||||||
'qty': 1.5,
|
],
|
||||||
})],
|
}
|
||||||
})
|
)
|
||||||
# Block because of exception during validation
|
# Block because of exception during validation
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
potest1.button_confirm()
|
potest1.button_confirm()
|
||||||
|
@ -73,4 +82,4 @@ class TestBaseException(common.SavepointCase):
|
||||||
# Test ignore exeception make possible for the po to validate
|
# Test ignore exeception make possible for the po to validate
|
||||||
potest1.ignore_exception = True
|
potest1.ignore_exception = True
|
||||||
potest1.button_confirm()
|
potest1.button_confirm()
|
||||||
self.assertTrue(potest1.state == 'purchase')
|
self.assertTrue(potest1.state == "purchase")
|
||||||
|
|
|
@ -1,6 +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>
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|
|
||||||
from . import base_exception_confirm
|
from . import base_exception_confirm
|
||||||
|
|
|
@ -2,39 +2,36 @@
|
||||||
# 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'
|
_description = "Exception Rule Confirm Wizard"
|
||||||
|
|
||||||
related_model_id = fields.Many2one('base.exception',)
|
related_model_id = fields.Many2one("base.exception")
|
||||||
exception_ids = fields.Many2many(
|
exception_ids = fields.Many2many(
|
||||||
'exception.rule',
|
"exception.rule", string="Exceptions to resolve", readonly=True
|
||||||
string='Exceptions to resolve',
|
|
||||||
readonly=True,
|
|
||||||
)
|
)
|
||||||
ignore = fields.Boolean('Ignore Exceptions')
|
ignore = fields.Boolean("Ignore Exceptions")
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def default_get(self, field_list):
|
def default_get(self, field_list):
|
||||||
res = super(ExceptionRuleConfirm, self).default_get(field_list)
|
res = super(ExceptionRuleConfirm, self).default_get(field_list)
|
||||||
current_model = self.env.context.get('active_model')
|
current_model = self.env.context.get("active_model")
|
||||||
model_except_obj = self.env[current_model]
|
model_except_obj = self.env[current_model]
|
||||||
active_ids = self.env.context.get('active_ids')
|
active_ids = self.env.context.get("active_ids")
|
||||||
if len(active_ids) > 1:
|
if len(active_ids) > 1:
|
||||||
raise ValidationError(
|
raise ValidationError(_("Only 1 ID accepted, got %r.") % active_ids)
|
||||||
_('Only 1 ID accepted, got %r.') % active_ids)
|
|
||||||
active_id = active_ids[0]
|
active_id = active_ids[0]
|
||||||
related_model_except = model_except_obj.browse(active_id)
|
related_model_except = model_except_obj.browse(active_id)
|
||||||
exception_ids = related_model_except.exception_ids.ids
|
exception_ids = related_model_except.exception_ids.ids
|
||||||
res.update({'exception_ids': [(6, 0, exception_ids)]})
|
res.update({"exception_ids": [(6, 0, exception_ids)]})
|
||||||
res.update({'related_model_id': active_id})
|
res.update({"related_model_id": active_id})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def action_confirm(self):
|
def action_confirm(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
return {'type': 'ir.actions.act_window_close'}
|
return {"type": "ir.actions.act_window_close"}
|
||||||
|
|
Loading…
Reference in New Issue