From 6d852c6f481c72ae0c0b6f99d02a18740dee70e3 Mon Sep 17 00:00:00 2001 From: MonsieurB Date: Tue, 11 Dec 2018 11:42:12 +0100 Subject: [PATCH 1/4] merge pr #1253 --- onchange_helper/README.rst | 11 ++++ onchange_helper/models/__init__.py | 1 - onchange_helper/models/base.py | 86 +++++++++++++++--------------- 3 files changed, 55 insertions(+), 43 deletions(-) diff --git a/onchange_helper/README.rst b/onchange_helper/README.rst index 622fbe8dd..7d70feca6 100644 --- a/onchange_helper/README.rst +++ b/onchange_helper/README.rst @@ -24,6 +24,17 @@ Example if you want to create a sale order and you want to get the values relati Then, `vals` will be updated with partner_invoice_id, partner_shipping_id, pricelist_id, etc... +You can also use it on existing record for example: + + `vals = {'partner_shipping_id': 1}` + + `vals = sale.play_onchanges(vals, ['partner_shipping_id'])` + +Then the onchange will be played with the vals passed and the existing vals of the sale. `vals` will be updated with partner_invoice_id, pricelist_id, etc.. + + +Behind the scene, `play_onchanges` will execute **all the methods** registered for the list of changed fields, so you do not have to call manually each onchange. To avoid performance issue when the method is called on a record, the record will be transformed into a memory record before calling the registered methods to avoid to trigger SQL updates command when values are assigned to the record by the onchange + Bug Tracker =========== diff --git a/onchange_helper/models/__init__.py b/onchange_helper/models/__init__.py index 08f5a5618..2c7ece331 100644 --- a/onchange_helper/models/__init__.py +++ b/onchange_helper/models/__init__.py @@ -1,3 +1,2 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - from . import base diff --git a/onchange_helper/models/base.py b/onchange_helper/models/base.py index fdc0457a3..8386567f6 100644 --- a/onchange_helper/models/base.py +++ b/onchange_helper/models/base.py @@ -2,50 +2,52 @@ # Copyright 2016-2017 Camptocamp (http://www.camptocamp.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, models - - -def get_new_values(model, record, on_change_result): - vals = on_change_result.get('value', {}) - new_values = {} - for fieldname, value in vals.items(): - if fieldname not in record: - column = model._fields[fieldname] - if value and column.type == 'many2one': - value = value[0] # many2one are tuple (id, name) - new_values[fieldname] = value - return new_values - - -@api.model -def play_onchanges(self, values, onchange_fields): - onchange_specs = self._onchange_spec() - # we need all fields in the dict even the empty ones - # otherwise 'onchange()' will not apply changes to them - all_values = values.copy() - for field in self._fields: - if field not in all_values: - all_values[field] = False - - # we work on a temporary record - new_record = self.new(all_values) - - new_values = {} - for field in onchange_fields: - onchange_values = new_record.onchange(all_values, - field, onchange_specs) - new_values.update(get_new_values(self, values, onchange_values)) - all_values.update(new_values) - - res = {f: v for f, v in all_values.items() - if f in values or f in new_values} - return res +from odoo import api, models class Base(models.AbstractModel): _inherit = 'base' - def _setup_complete(self): - if not hasattr(models.BaseModel, 'play_onchanges'): - setattr(models.BaseModel, 'play_onchanges', play_onchanges) - return super(Base, self)._setup_complete() + @api.model + def _get_new_values(self, record, on_change_result): + vals = on_change_result.get('value', {}) + new_values = {} + for fieldname, value in vals.iteritems(): + if fieldname not in record: + column = self._fields[fieldname] + if value and column.type == 'many2one': + value = value[0] # many2one are tuple (id, name) + new_values[fieldname] = value + return new_values + + def play_onchanges(self, values, onchange_fields): + onchange_specs = self._onchange_spec() + # we need all fields in the dict even the empty ones + # otherwise 'onchange()' will not apply changes to them + all_values = values.copy() + for field in self._fields: + if field not in all_values: + all_values[field] = False + + # If self is a record (play onchange on existing record) + # we take the value of the field + # If self is an empty record we will have an empty value + if self: + self.ensure_one() + record_values = self._convert_to_write(self.read()[0]) + else: + record_values = {} + for field in self._fields: + if field not in all_values: + all_values[field] = record_values.get(field, False) + + new_values = {} + for field in onchange_fields: + onchange_values = self.onchange(all_values, field, onchange_specs) + new_values.update(self._get_new_values(values, onchange_values)) + all_values.update(new_values) + + return { + f: v for f, v in all_values.iteritems() + if not self._fields[f].compute + and (f in values or f in new_values)} From b1718a5cb74de29b04cd8f82200066592e7aa3d1 Mon Sep 17 00:00:00 2001 From: MonsieurB Date: Tue, 11 Dec 2018 17:19:10 +0100 Subject: [PATCH 2/4] port to 11.0 --- onchange_helper/models/base.py | 12 +++---- onchange_helper/tests/__init__.py | 1 - onchange_helper/tests/test_onchange_helper.py | 31 +++++-------------- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/onchange_helper/models/base.py b/onchange_helper/models/base.py index 8386567f6..f137355ee 100644 --- a/onchange_helper/models/base.py +++ b/onchange_helper/models/base.py @@ -12,7 +12,7 @@ class Base(models.AbstractModel): def _get_new_values(self, record, on_change_result): vals = on_change_result.get('value', {}) new_values = {} - for fieldname, value in vals.iteritems(): + for fieldname, value in vals.items(): if fieldname not in record: column = self._fields[fieldname] if value and column.type == 'many2one': @@ -25,10 +25,6 @@ class Base(models.AbstractModel): # we need all fields in the dict even the empty ones # otherwise 'onchange()' will not apply changes to them all_values = values.copy() - for field in self._fields: - if field not in all_values: - all_values[field] = False - # If self is a record (play onchange on existing record) # we take the value of the field # If self is an empty record we will have an empty value @@ -48,6 +44,8 @@ class Base(models.AbstractModel): all_values.update(new_values) return { - f: v for f, v in all_values.iteritems() + f: v for f, v in all_values.items() if not self._fields[f].compute - and (f in values or f in new_values)} + and (f in values or f in new_values)} + + diff --git a/onchange_helper/tests/__init__.py b/onchange_helper/tests/__init__.py index 806c1a0d6..6bcec9ff9 100644 --- a/onchange_helper/tests/__init__.py +++ b/onchange_helper/tests/__init__.py @@ -1,3 +1,2 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - from . import test_onchange_helper diff --git a/onchange_helper/tests/test_onchange_helper.py b/onchange_helper/tests/test_onchange_helper.py index adcff2c1a..86c6fdb88 100644 --- a/onchange_helper/tests/test_onchange_helper.py +++ b/onchange_helper/tests/test_onchange_helper.py @@ -1,28 +1,13 @@ # Copyright 2017 Onestein () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase +#from odoo.tests.common import TransactionCase +import odoo.tests.common as common +class TestOnchangeHelper(common.TransactionCase): -class TestOnchangeHelper(TransactionCase): - - def test01_partner_parent(self): - main_partner = self.env.ref('base.main_partner') - input_vals = dict(partner_id=main_partner.id) - updated_vals = self.env['res.partner'].play_onchanges( - input_vals, - ['parent_id'] - ) - self.assertIn('commercial_partner_id', updated_vals) - self.assertIn('display_name', updated_vals) - self.assertIn('partner_id', updated_vals) - - def test02_partner_country(self): - partner_demo = self.env.ref('base.partner_demo') - input_vals = {'partner_id': partner_demo.id} - updated_vals = self.env['res.partner'].play_onchanges( - input_vals, - ['country_id'] - ) - self.assertIn('contact_address', updated_vals) - self.assertIn('partner_id', updated_vals) + def test_playing_onchange_on_model(self): + result = self.env['res.partner'].play_onchanges({ + 'company_type': 'company', + }, ['company_type']) + self.assertEqual(result['is_company'], True) From 8e7cbba98dd3b1a90ebe32dd6da8b81e5c7626f1 Mon Sep 17 00:00:00 2001 From: MonsieurB Date: Tue, 11 Dec 2018 17:23:18 +0100 Subject: [PATCH 3/4] flake 8 --- onchange_helper/models/base.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/onchange_helper/models/base.py b/onchange_helper/models/base.py index f137355ee..756b7d6ce 100644 --- a/onchange_helper/models/base.py +++ b/onchange_helper/models/base.py @@ -2,9 +2,9 @@ # Copyright 2016-2017 Camptocamp (http://www.camptocamp.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - from odoo import api, models + class Base(models.AbstractModel): _inherit = 'base' @@ -46,6 +46,4 @@ class Base(models.AbstractModel): return { f: v for f, v in all_values.items() if not self._fields[f].compute - and (f in values or f in new_values)} - - + and (f in values or f in new_values)} From 047c3a27a2c0a52d167ff8c3c62b2f15ee6d25d5 Mon Sep 17 00:00:00 2001 From: MonsieurB Date: Thu, 20 Dec 2018 14:52:21 +0100 Subject: [PATCH 4/4] Fix pylint on tests --- onchange_helper/tests/test_onchange_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onchange_helper/tests/test_onchange_helper.py b/onchange_helper/tests/test_onchange_helper.py index 86c6fdb88..709771205 100644 --- a/onchange_helper/tests/test_onchange_helper.py +++ b/onchange_helper/tests/test_onchange_helper.py @@ -1,9 +1,9 @@ # Copyright 2017 Onestein () # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -#from odoo.tests.common import TransactionCase import odoo.tests.common as common + class TestOnchangeHelper(common.TransactionCase): def test_playing_onchange_on_model(self):