diff --git a/base_exception/__init__.py b/base_exception/__init__.py index 7f16dfdc2..75a695531 100644 --- a/base_exception/__init__.py +++ b/base_exception/__init__.py @@ -1,5 +1,2 @@ -# Copyright 2011 Raphaƫl Valyi, Renato Lima, Guewen Baconnier, Sodexis -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import wizard, models -from .tests import test_tmp_model diff --git a/base_exception/models/base_exception.py b/base_exception/models/base_exception.py index 0eebb72cf..e263ff7a7 100644 --- a/base_exception/models/base_exception.py +++ b/base_exception/models/base_exception.py @@ -5,11 +5,14 @@ import time from functools import wraps +from odoo import api, fields, models, _ +import logging -from odoo import api, models, fields, _ from odoo.exceptions import UserError, ValidationError from odoo.tools.safe_eval import safe_eval +_logger = logging.getLogger(__name__) + def implemented_by_base_exception(func): """Call a prefixed function based on 'namespace'.""" @@ -45,15 +48,14 @@ class ExceptionRule(models.Model): active = fields.Boolean('Active') next_state = fields.Char( 'Next state', - help="If we detect exception we set de 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 " "exception detected and all have a value for next_state, we use" "the exception having the smallest sequence value") code = fields.Text( 'Python Code', help="Python code executed to check if the exception apply or " - "not. The code must apply block = True to apply 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 : @@ -63,7 +65,7 @@ class ExceptionRule(models.Model): # 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 -# - pool: ORM model pool (i.e. self.pool) +# - env: Odoo Environment (i.e. self.env) # - time: Python time module # - cr: database cursor # - uid: current user id @@ -79,10 +81,10 @@ class ExceptionRule(models.Model): select_vals = self.env[ rule.model].fields_get()[ 'state']['selection'] - if rule.next_state\ - not in [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: raise ValidationError( - _('The value "%s" you chose for the "next state" ' + _('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, @@ -121,14 +123,14 @@ class BaseException(models.AbstractModel): @api.multi def _popup_exceptions(self): action = self._get_popup_action() - action = action.read()[0] - action.update({ + action_data = action.read()[0] + action_data.update({ 'context': { 'active_id': self.ids[0], 'active_ids': self.ids } }) - return action + return action_data @api.model def _get_popup_action(self): @@ -187,18 +189,17 @@ class BaseException(models.AbstractModel): @api.model def _exception_rule_eval_context(self, obj_name, rec): - user = self.env['res.users'].browse(self._uid) return {obj_name: rec, - 'self': self.pool.get(rec._name), + 'self': self.env[rec._name], 'object': rec, 'obj': rec, - 'pool': self.pool, - 'cr': self._cr, - 'uid': self._uid, - 'user': user, + 'env': self.env, + 'cr': self.env.cr, + 'uid': self.env.user.id, + 'user': self.env.user, 'time': time, # copy context to prevent side-effects of eval - 'context': self._context.copy()} + 'context': self.env.context.copy()} @api.model def _rule_eval(self, rule, obj_name, rec): @@ -225,10 +226,8 @@ class BaseException(models.AbstractModel): if self._rule_eval(rule, self.rule_group, self): exception_ids.append(rule.id) if rule.next_state: - if not next_state_rule: - next_state_rule = rule - elif next_state_rule and\ - rule.sequence < next_state_rule.sequence: + if not next_state_rule or\ + rule.sequence < next_state_rule.sequence: next_state_rule = rule if sub_exceptions: for obj_line in self._get_lines(): diff --git a/base_exception/security/ir.model.access.csv b/base_exception/security/ir.model.access.csv index ad0cb33f9..38331a4f4 100644 --- a/base_exception/security/ir.model.access.csv +++ b/base_exception/security/ir.model.access.csv @@ -3,5 +3,3 @@ access_exception_rule,base.exception,model_exception_rule,base.group_user,1,0,0, access_exception_rule_manager,base.exception,model_exception_rule,base_exception.group_exception_rule_manager,1,1,1,1 access_base_exception,base.exception,model_base_exception,base.group_user,1,0,0,0 access_base_exception_manager,base.exception,model_base_exception,base_exception.group_exception_rule_manager,1,1,1,1 -access_base_exception_test_purchase,access_base_exception_test_purchase,model_base_exception_test_purchase,base.group_system,1,1,1,1 -access_base_exception_test_model_line,access_base_exception_test_model_line,model_base_exception_test_model_line,base.group_system,1,1,1,1 \ No newline at end of file diff --git a/base_exception/tests/__init__.py b/base_exception/tests/__init__.py index e817e312c..874f27874 100644 --- a/base_exception/tests/__init__.py +++ b/base_exception/tests/__init__.py @@ -1,3 +1,4 @@ -from . import test_tmp_model +from . import common +from . import purchase_test from . import test_base_exception diff --git a/base_exception/tests/common.py b/base_exception/tests/common.py new file mode 100644 index 000000000..fc52552f8 --- /dev/null +++ b/base_exception/tests/common.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV () +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +def setup_test_model(env, model_clses): + for model_cls in model_clses: + model_cls._build_model(env.registry, env.cr) + + env.registry.setup_models(env.cr) + env.registry.init_models( + env.cr, [model_cls._name for model_cls in model_clses], + dict(env.context, update_custom_fields=True) + ) diff --git a/base_exception/tests/test_tmp_model.py b/base_exception/tests/purchase_test.py similarity index 52% rename from base_exception/tests/test_tmp_model.py rename to base_exception/tests/purchase_test.py index a07367878..715b200bc 100644 --- a/base_exception/tests/test_tmp_model.py +++ b/base_exception/tests/purchase_test.py @@ -1,7 +1,6 @@ -# Copyright 2017 Akretion (http://www.akretion.com) -# Mourad EL HADJ MIMOUNE - -from odoo import fields, models, api +# Copyright 2016 Akretion Mourad EL HADJ MIMOUNE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models class PurchaseTest(models.Model): @@ -22,50 +21,53 @@ class PurchaseTest(models.Model): string="Status", readonly=True, default='draft') active = fields.Boolean(default=True) partner_id = fields.Many2one('res.partner', string='Partner') - line_ids = fields.One2many('base.exception.test.model.line', 'lead_id') - amount_total = fields.Float(compute='_compute_amount_total', store=True) + line_ids = fields.One2many( + 'base.exception.test.purchase.line', 'lead_id') + amount_total = fields.Float( + compute='_compute_amount_total', store=True) @api.depends('line_ids') - def _compute_amount_total(self): - for record in self: + def _compute_amount_total(cls): + for record in cls: for line in record.line_ids: record.amount_total += line.amount * line.qty @api.constrains('ignore_exception', 'line_ids', 'state') - def test_purchase_check_exception(self): - orders = self.filtered(lambda s: s.state == 'purchase') + def test_purchase_check_exception(cls): + orders = cls.filtered(lambda s: s.state == 'purchase') if orders: orders._check_exception() @api.multi - def button_approve(self, force=False): - self.write({'state': 'to approve'}) + def button_approve(cls, force=False): + cls.write({'state': 'to approve'}) return {} @api.multi - def button_draft(self): - self.write({'state': 'draft'}) + def button_draft(cls): + cls.write({'state': 'draft'}) return {} @api.multi - def button_confirm(self): - self.write({'state': 'purchase'}) + def button_confirm(cls): + cls.write({'state': 'purchase'}) return True @api.multi - def button_cancel(self): - self.write({'state': 'cancel'}) + def button_cancel(cls): + cls.write({'state': 'cancel'}) - def test_base_get_lines(self): - self.ensure_one() - return self.line_ids + def test_base_get_lines(cls): + cls.ensure_one() + return cls.line_ids class LineTest(models.Model): - _name = "base.exception.test.model.line" - _description = "Base Ecxeption Test Model Line" + _name = "base.exception.test.purchase.line" + _description = "Base Exception Test Model Line" name = fields.Char() - lead_id = fields.Many2one('base.exception.test.model', ondelete='cascade') + lead_id = fields.Many2one('base.exception.test.purchase', + ondelete='cascade') qty = fields.Float() amount = fields.Float() diff --git a/base_exception/tests/test_base_exception.py b/base_exception/tests/test_base_exception.py index e8f57d751..33755133d 100644 --- a/base_exception/tests/test_base_exception.py +++ b/base_exception/tests/test_base_exception.py @@ -2,35 +2,38 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from odoo.tests import common - +from .common import setup_test_model +from .purchase_test import PurchaseTest, LineTest import logging _logger = logging.getLogger(__name__) -# @common.at_install(False) -# @common.post_install(True) -class TestBaseException(common.TransactionCase): +class TestBaseException(common.SavepointCase): - def setUp(self): - super(TestBaseException, self).setUp() + # pylint: disable=missing-return - self.base_exception = self.env['base.exception'] - self.exception_rule = self.env['exception.rule'] - self.exception_confirm = self.env['exception.rule.confirm'] + @classmethod + def setUpClass(cls): + super(TestBaseException, cls).setUpClass() + setup_test_model(cls.env, [PurchaseTest, LineTest]) - self.exception_rule._fields['rule_group'].selection.append( + cls.base_exception = cls.env['base.exception'] + cls.exception_rule = cls.env['exception.rule'] + cls.exception_confirm = cls.env['exception.rule.confirm'] + + cls.exception_rule._fields['rule_group'].selection.append( ('test_base', 'test base exception') ) - self.exception_rule._fields['model'].selection.append( + cls.exception_rule._fields['model'].selection.append( ('base.exception.test.purchase', 'base.exception.test.purchase') ) - self.exception_rule._fields['model'].selection.append( + cls.exception_rule._fields['model'].selection.append( ('base.exception.test.purchase.line', 'base.exception.test.purchase.line') ) - self.exceptionnozip = self.env['exception.rule'].create({ + cls.exceptionnozip = cls.env['exception.rule'].create({ 'name': "No ZIP code on destination", 'sequence': 10, 'rule_group': "test_base", @@ -38,7 +41,7 @@ class TestBaseException(common.TransactionCase): 'code': """if not test_base.partner_id.zip: failed=True""", }) - self.exceptionno_minorder = self.env['exception.rule'].create({ + cls.exceptionno_minorder = cls.env['exception.rule'].create({ 'name': "Min order except", 'sequence': 10, 'rule_group': "test_base", @@ -47,7 +50,7 @@ class TestBaseException(common.TransactionCase): failed=True""", }) - self.exceptionno_lineqty = self.env['exception.rule'].create({ + cls.exceptionno_lineqty = cls.env['exception.rule'].create({ 'name': "Qty > 0", 'sequence': 10, 'rule_group': "test_base", @@ -55,7 +58,7 @@ class TestBaseException(common.TransactionCase): 'code': """if test_base_line.qty <= 0: failed=True"""}) - def test_sale_order_exception(self): + def test_purchase_order_exception(self): partner = self.env.ref('base.res_partner_1') partner.zip = False potest1 = self.env['base.exception.test.purchase'].create({ diff --git a/base_exception/wizard/base_exception_confirm.py b/base_exception/wizard/base_exception_confirm.py index 9cb207f62..4ab485b8f 100644 --- a/base_exception/wizard/base_exception_confirm.py +++ b/base_exception/wizard/base_exception_confirm.py @@ -3,7 +3,8 @@ # Mourad EL HADJ MIMOUNE # 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 class ExceptionRuleConfirm(models.AbstractModel): @@ -19,13 +20,15 @@ class ExceptionRuleConfirm(models.AbstractModel): @api.model def default_get(self, field_list): res = super(ExceptionRuleConfirm, self).default_get(field_list) - current_model = self._context.get('active_model') + current_model = self.env.context.get('active_model') model_except_obj = self.env[current_model] - active_ids = self._context.get('active_ids') - assert len(active_ids) == 1, "Only 1 ID accepted, got %r" % active_ids + active_ids = self.env.context.get('active_ids') + if len(active_ids) > 1: + raise ValidationError( + _('Only 1 ID accepted, got %r.') % active_ids) active_id = active_ids[0] related_model_except = model_except_obj.browse(active_id) - exception_ids = [e.id for e in related_model_except.exception_ids] + exception_ids = related_model_except.exception_ids.ids res.update({'exception_ids': [(6, 0, exception_ids)]}) res.update({'related_model_id': active_id}) return res diff --git a/base_exception/wizard/base_exception_confirm_view.xml b/base_exception/wizard/base_exception_confirm_view.xml index e100c7ac6..a8ece8e33 100644 --- a/base_exception/wizard/base_exception_confirm_view.xml +++ b/base_exception/wizard/base_exception_confirm_view.xml @@ -33,5 +33,5 @@ form new - +