diff --git a/onchange_helper/__manifest__.py b/onchange_helper/__manifest__.py index 993f7bc7b..d220bd481 100644 --- a/onchange_helper/__manifest__.py +++ b/onchange_helper/__manifest__.py @@ -1,13 +1,14 @@ # Copyright 2016-2017 Akretion (http://www.akretion.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -{'name': 'Onchange Helper', - 'version': '12.0.1.0.0', - 'summary': 'Technical module that ease execution of onchange in Python code', - 'author': 'Akretion,Camptocamp,Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/server-tools', - 'license': 'AGPL-3', - 'category': 'Generic Modules', - 'depends': ['base'], - 'installable': True, - } +{ + 'name': 'Onchange Helper', + 'version': '12.0.1.0.0', + 'summary': 'Technical module that ease execution of onchange in Python code', + 'author': 'Akretion,Camptocamp,Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/server-tools', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'depends': ['base'], + 'installable': True, +} diff --git a/onchange_helper/models/base.py b/onchange_helper/models/base.py index fdc0457a3..d0823dbb0 100644 --- a/onchange_helper/models/base.py +++ b/onchange_helper/models/base.py @@ -5,47 +5,47 @@ 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 - - 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.items(): + 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 + + @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() + # 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.items() + if not self._fields[f].compute and (f in values or f in new_values) + } diff --git a/onchange_helper/readme/USAGE.rst b/onchange_helper/readme/USAGE.rst index 183b6e728..82a4c163f 100644 --- a/onchange_helper/readme/USAGE.rst +++ b/onchange_helper/readme/USAGE.rst @@ -10,3 +10,13 @@ Example if you want to create a sale order and you want to get the values relati `vals = self.env['sale.order'].play_onchanges(vals, ['partner_id'])` 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 diff --git a/onchange_helper/tests/test_onchange_helper.py b/onchange_helper/tests/test_onchange_helper.py index adcff2c1a..848da864c 100644 --- a/onchange_helper/tests/test_onchange_helper.py +++ b/onchange_helper/tests/test_onchange_helper.py @@ -5,13 +5,11 @@ from odoo.tests.common import 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'] + input_vals, ['parent_id'] ) self.assertIn('commercial_partner_id', updated_vals) self.assertIn('display_name', updated_vals) @@ -21,8 +19,25 @@ class TestOnchangeHelper(TransactionCase): 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'] + 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) + + def test_playing_onchange_on_record(self): + company = self.env.ref('base.main_company') + result = company.play_onchanges( + {'email': 'contact@akretion.com'}, ['email'] + ) + self.assertEqual( + result['rml_footer'], + u'Phone: +1 555 123 8069 | Email: contact@akretion.com | ' + u'Website: http://www.example.com', + ) + self.assertEqual(company.email, u'info@yourcompany.example.com')