From 54968d55c243d9890804305915ee72b6fe44c2ae Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 14 Sep 2016 13:04:49 +0200 Subject: [PATCH 01/14] [FIX] tools: do not overwrite view priority on update In some cases, users change the sequence of base views in order to have one applied before the other e.g. when there is two inherited views doing ``` ... ``` The priority of the views will have an impact on the fields order in the tree. On update, the change of priority done by the users was overwritten, even if the priority of the view was not defined in the XML code of the view. The priority of the views should be overwritten only in the case were the priority of the view is specifically set in its definition. opw-688470 --- openerp/tools/convert.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/tools/convert.py b/openerp/tools/convert.py index 314d946cd69..3fcd781e4a0 100644 --- a/openerp/tools/convert.py +++ b/openerp/tools/convert.py @@ -741,7 +741,8 @@ form: module.record_id""" % (xml_id,) record.append(Field(name, name='name')) record.append(Field(full_tpl_id, name='key')) record.append(Field("qweb", name='type')) - record.append(Field(el.get('priority', "16"), name='priority')) + if 'priority' in el.attrib: + record.append(Field(el.get('priority'), name='priority')) if 'inherit_id' in el.attrib: record.append(Field(name='inherit_id', ref=el.get('inherit_id'))) if 'website_id' in el.attrib: From 8914e7f48c84c891f055e44fec5ca4c7f23329c7 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Wed, 24 Aug 2016 12:07:04 +0200 Subject: [PATCH 02/14] [FIX] stock: preserve proposed serial/lot numbers after put in pack When a stock.pack.operation gets created by a stock.picking, it will have its pack_lot_ids set. This way the user has an overview of what lots are available and how many of each are still left. When splitting the stock.pack.operation these where lost. The still unpacked stock.pack.operation would always have an empty pack_lot_ids field. The reason for this is that those records where reassigned to the newly created and packed stock.pack.operation. This resolves the issue by copying pack_lot_ids that are still available (qty_todo > 0) and assigning them to the original stock.pack.operation. opw-685908 --- addons/stock/stock.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7135ca695fc..686b5ad51a8 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1732,6 +1732,7 @@ class stock_picking(models.Model): def put_in_pack(self, cr, uid, ids, context=None): stock_move_obj = self.pool["stock.move"] stock_operation_obj = self.pool["stock.pack.operation"] + stock_operation_lot_obj = self.pool["stock.pack.operation.lot"] package_obj = self.pool["stock.quant.package"] package_id = False for pick in self.browse(cr, uid, ids, context=context): @@ -1748,6 +1749,16 @@ class stock_picking(models.Model): packlots_transfer = [(4, x.id) for x in operation.pack_lot_ids] stock_operation_obj.write(cr, uid, [new_operation], {'pack_lot_ids': packlots_transfer}, context=context) + # the stock.pack.operation.lot records now belong to the new, packaged stock.pack.operation + # we have to create new ones with new quantities for our original, unfinished stock.pack.operation + for pack_lot_id in stock_operation_obj.browse(cr, uid, new_operation, context=context).pack_lot_ids: + new_qty_todo = pack_lot_id.qty_todo - pack_lot_id.qty + + if new_qty_todo > 0: + stock_operation_lot_obj.copy(cr, uid, pack_lot_id.id, {'operation_id': operation.id, + 'qty_todo': new_qty_todo, + 'qty': 0}, context=context) + op = stock_operation_obj.browse(cr, uid, new_operation, context=context) pack_operation_ids.append(op.id) if operations: From 9a5b040ff847458388435bd5f68aa5d7abfb3f44 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Fri, 26 Aug 2016 14:02:06 +0200 Subject: [PATCH 03/14] [FIX] stock: copy over pack_lot_ids when clicking on 'split' Analogous to 6fde72abeacdac6e36a0c623c024252839f993ae but now instead for the 'split' button. Additionally this uses float_compare with the rounding set on the uom of the operation to determine if there are any units left, which is more correct. opw-685908 --- addons/stock/stock.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 686b5ad51a8..f0016b5cd1e 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1732,7 +1732,6 @@ class stock_picking(models.Model): def put_in_pack(self, cr, uid, ids, context=None): stock_move_obj = self.pool["stock.move"] stock_operation_obj = self.pool["stock.pack.operation"] - stock_operation_lot_obj = self.pool["stock.pack.operation.lot"] package_obj = self.pool["stock.quant.package"] package_id = False for pick in self.browse(cr, uid, ids, context=context): @@ -1751,13 +1750,7 @@ class stock_picking(models.Model): # the stock.pack.operation.lot records now belong to the new, packaged stock.pack.operation # we have to create new ones with new quantities for our original, unfinished stock.pack.operation - for pack_lot_id in stock_operation_obj.browse(cr, uid, new_operation, context=context).pack_lot_ids: - new_qty_todo = pack_lot_id.qty_todo - pack_lot_id.qty - - if new_qty_todo > 0: - stock_operation_lot_obj.copy(cr, uid, pack_lot_id.id, {'operation_id': operation.id, - 'qty_todo': new_qty_todo, - 'qty': 0}, context=context) + stock_operation_obj._copy_remaining_pack_lot_ids(cr, uid, new_operation, operation.id, context=context) op = stock_operation_obj.browse(cr, uid, new_operation, context=context) pack_operation_ids.append(op.id) @@ -4523,6 +4516,7 @@ class stock_pack_operation(osv.osv): if pack.product_qty - pack.qty_done > 0.0 and pack.qty_done < pack.product_qty: pack2 = self.copy(cr, uid, pack.id, default={'qty_done': 0.0, 'product_qty': pack.product_qty - pack.qty_done}, context=context) self.write(cr, uid, [pack.id], {'product_qty': pack.qty_done}, context=context) + self._copy_remaining_pack_lot_ids(cr, uid, pack.id, pack2, context=context) else: raise UserError(_('The quantity to split should be smaller than the quantity To Do. ')) return True @@ -4607,6 +4601,17 @@ class stock_pack_operation(osv.osv): 'context': context, } + def _copy_remaining_pack_lot_ids(self, cr, uid, id, new_operation_id, context=None): + stock_pack_operation_lot_obj = self.pool["stock.pack.operation.lot"] + old_operation = self.browse(cr, uid, id, context=context) + + for pack_lot_id in old_operation.pack_lot_ids: + new_qty_todo = pack_lot_id.qty_todo - pack_lot_id.qty + + if float_compare(new_qty_todo, 0, precision_rounding=old_operation.product_uom_id.rounding) > 0: + stock_pack_operation_lot_obj.copy(cr, uid, pack_lot_id.id, {'operation_id': new_operation_id, + 'qty_todo': new_qty_todo, + 'qty': 0}, context=context) class stock_pack_operation_lot(osv.osv): _name = "stock.pack.operation.lot" From 1da79b872e7b90b67a097ed50b974c879f26bea2 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 14 Sep 2016 14:33:50 +0200 Subject: [PATCH 04/14] [FIX] web: keep m2m value in list view Currently, when rendering a list view cell with a many2many we would empty the list of ids, and fill it again once a name_get is resolved. But in some instance, the code could use the data when it has been emptied out. For example, if we set the tax_id field (inside the order_line list view inside the sale.order form view) as requred, if we modify the order line and save directly (without clicking outside of the list view) we can get an incorrect error saying that the "Order Line" is not valid. It has been reproduced when saving with CTRL + SHIFT + S on google chrome and firefox, and there have been reports that for some configuration it also happen when clicking on the "Save" button. This commit change the behaviour so the value is kept whilst the name_get is ongoing, and just use a default "false" value for the name during this interval. closes #13478 opw-668067 --- addons/web/static/src/js/view_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index c460cc598f0..9fe5426f2cc 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -1125,8 +1125,8 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. _(names).pluck(1).join(', ')); record.set(column.id, ids); }); - // temp empty value - record.set(column.id, false); + // temporary empty display name + record.set(column.id + '__display', false); } } return column.format(record.toForm().data, { From 07a207c07c8252bccb0216b0716a2fd4f975f77d Mon Sep 17 00:00:00 2001 From: andreparames Date: Thu, 15 Sep 2016 07:15:34 +0100 Subject: [PATCH 05/14] [FIX] ETag value must be quote-delimited According to RFC 7232 # 2.3, an etag must be wrapped in double quotes: entity-tag = [ weak ] opaque-tag opaque-tag = DQUOTE *etagc DQUOTE etagc = %x21 / %x23-7E / obs-text ; VCHAR except double quotes, plus obs-text Odoo didn't properly quote etags, which could lead to stripping or failures when putting Odoo behind strict HTTP proxies. --- openerp/addons/base/ir/ir_http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index d8ece72e1b1..dde75a64a76 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -302,7 +302,7 @@ class ir_http(osv.AbstractModel): # cache etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match') - retag = hashlib.md5(last_update).hexdigest() + retag = '"%s"' % hashlib.md5(last_update).hexdigest() status = status or (304 if etag == retag else 200) headers.append(('ETag', retag)) headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0))) From 9874892b97c6fd93c94465a7d0df6e434c05d909 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 13 Sep 2016 15:04:58 +0200 Subject: [PATCH 06/14] [FIX] module#get_module_path() does not check the manifest's presence (closes #13467) --- openerp/modules/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 8a72fd6f954..e9e643001f0 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -121,7 +121,7 @@ def get_module_path(module, downloaded=False, display_warning=True): """ initialize_sys_path() for adp in ad_paths: - if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)): + if os.path.exists(opj(adp, module, MANIFEST)) or os.path.exists(opj(adp, '%s.zip' % module)): return opj(adp, module) if downloaded: From 143ec3739196f7f69ad678822248850f9a3f6a32 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 13 Sep 2016 15:20:49 +0200 Subject: [PATCH 07/14] [IMP] account: compute_all rounding In some specific cases,it is necessary to call the `compute_all` method and prevent any rounding to be done. This adds some flexibility in the method to override rounding thanks to a 'round' context key. Related to #13452 and commit 2e4777bb3288b opw-688399 --- addons/account/models/account.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 1d1d50cfc01..68b245da113 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -669,7 +669,18 @@ class AccountTax(models.Model): # the 'Account' decimal precision + 5), and that way it's like # rounding after the sum of the tax amounts of each line prec = currency.decimal_places - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + + # In some cases, it is necessary to force/prevent the rounding of the tax and the total + # amounts. For example, in SO/PO line, we don't want to round the price unit at the + # precision of the currency. + # The context key 'round' allows to force the standard behavior. + round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True + round_total = True + if 'round' in self.env.context: + round_tax = bool(self.env.context['round']) + round_total = bool(self.env.context['round']) + + if not round_tax: prec += 5 total_excluded = total_included = base = round(price_unit * quantity, prec) @@ -688,7 +699,7 @@ class AccountTax(models.Model): continue tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) - if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): + if not round_tax: tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) @@ -714,8 +725,8 @@ class AccountTax(models.Model): return { 'taxes': sorted(taxes, key=lambda k: k['sequence']), - 'total_excluded': currency.round(total_excluded) if bool(self.env.context.get("round", True)) else total_excluded, - 'total_included': currency.round(total_included) if bool(self.env.context.get("round", True)) else total_included, + 'total_excluded': currency.round(total_excluded) if round_total else total_excluded, + 'total_included': currency.round(total_included) if round_total else total_included, 'base': base, } From c3f4c7395383856eff92637a8e95eb6535ed9364 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 15 Sep 2016 08:54:59 +0200 Subject: [PATCH 08/14] [FIX] account: monetaryIsZero An issue arises in the following use case: - Company rounding policy in "round globally" - Tax excluded of 20 % - Create a statement of 3.80 EUR - Reconcile the statement thanks to the reconciliation widget - Click on "Choose counterpart" in order to manually create a line to reconcile with the 3.80 EUR statement - Add an amount of 3.17 EUR, and choose the 20 % tax The widget creates an extra line of 0.00EUR . This is because the remaining amount is 0.004. The reason lies in the `monetaryIsZero` function. There is a hardcoded decimal precision of 4, while it should be 2 for EUR. We add an extra parameter to the function so it is possible to adjust the decimal precision required. Only the problematic calls are modified to avoid unnecessary intrusive modification. Closes #13452 opw-688399 --- .../src/js/account_reconciliation_widgets.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js index 22a2704dc2f..92e6cd6f3da 100644 --- a/addons/account/static/src/js/account_reconciliation_widgets.js +++ b/addons/account/static/src/js/account_reconciliation_widgets.js @@ -18,6 +18,7 @@ var pyeval = require('web.pyeval'); var web_client = require('web.web_client'); var parse_value = require('web.web_client'); var Widget = require('web.Widget'); +var session = require('web.session'); var FieldMany2One = core.form_widget_registry.get('many2one'); var FieldChar = core.form_widget_registry.get('char'); @@ -76,8 +77,6 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager }); this.crash_manager = new CrashManager(); - // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more - this.monetaryIsZero = _.partial(utils.float_is_zero, _, 4); this.formatCurrencies; // Method that formats the currency ; loaded from the server this.model_res_users = new Model("res.users"); this.model_tax = new Model("account.tax"); @@ -371,6 +370,11 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, { if (line.ref && line.ref !== line.name) line.q_label = line.q_label + " : " + line.ref; }, + + // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more + monetaryIsZero: function(amount, digits) { + return utils.float_is_zero(amount, digits === undefined ? 4 : digits); + }, }); var abstractReconciliationLine = Widget.extend({ @@ -1875,7 +1879,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ // Find out if the counterpart is lower than, equal or greater than the transaction being reconciled var balance_type = undefined; - if (self.monetaryIsZero(self.get("balance"))) balance_type = "equal"; + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (self.monetaryIsZero(self.get("balance"), digits)) balance_type = "equal"; else if (self.get("balance") * self.st_line.amount > 0) balance_type = "greater"; else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower"; @@ -2070,7 +2078,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({ var payment_aml = _.filter(this.get("mv_lines_selected"), function(line) { return line.already_paid }); var payment_aml_ids = _.collect(payment_aml, function(line) { return line.id }); var new_aml_dicts = this.prepareCreatedMoveLinesForPersisting(this.getCreatedLines()); - if (! self.monetaryIsZero(this.get("balance"))) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); + var digits = 4; + if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) { + digits = session.get_currency(this.get("currency_id")).digits[1]; + } + if (! self.monetaryIsZero(this.get("balance"), digits)) new_aml_dicts.push(this.prepareOpenBalanceForPersisting()); return { 'counterpart_aml_dicts': counterpart_aml_dicts, 'payment_aml_ids': payment_aml_ids, From ce3341a90611d66f93781a0c1cc3db9895afcc90 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 15 Sep 2016 09:50:59 +0200 Subject: [PATCH 09/14] [FIX] account: tax amount at reconciliation When doing a reconciliation, make sure that the tax amount is properly rounded to the currency precision. Otherwise, if the rounding policy of the company is "round globally", the amount recorded won't be rounded. opw-688399 Closes #13452 --- addons/account/models/account_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 6520897265d..766826e10d8 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1067,7 +1067,7 @@ class AccountMoveLine(models.Model): taxes = self.env['account.tax'].browse(tax_ids) currency = self.env['res.currency'].browse(vals.get('currency_id')) partner = self.env['res.partner'].browse(vals.get('partner_id')) - res = taxes.compute_all(amount, + res = taxes.with_context(round=True).compute_all(amount, currency, 1, vals.get('product_id'), partner) # Adjust line amount if any tax is price_include if abs(res['total_excluded']) < abs(amount): From 5a104fcb1a281e8bd5ab78be69437102188bda9c Mon Sep 17 00:00:00 2001 From: Mansi Gandhi Date: Tue, 28 Jun 2016 10:38:40 +0530 Subject: [PATCH 10/14] [FIX] website_quote: Notification 'viewed by customer' should be received by the responsible only Currently some customers receive the automatic notification 'Quotation viewed by customer'. This should not be the case as this notifciation is purely internal and should only be sent to the quote responsible. Notification is now a note with the responsible being directly in the recipients. Conflicts: addons/website_quote/controllers/main.py Backport of 14f07cb96fb06ef295c3476f96b73b2e5ff37a72 opw-687957 --- addons/website_quote/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index f472cbdfea3..a5e7b6c85ac 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -30,7 +30,7 @@ class sale_quote(http.Controller): if request.session.get('view_quote',False)!=now: request.session['view_quote'] = now body=_('Quotation viewed by customer') - _message_post_helper(res_model='sale.order', res_id=order.id, message=body, token=token, token_field="access_token", message_type='notification') + _message_post_helper(res_model='sale.order', res_id=order.id, message=body, token=token, token_field="access_token", message_type='notification', subtype="mail.mt_note", partner_ids=order.user_id.partner_id.ids) days = 0 if order.validity_date: days = (datetime.datetime.strptime(order.validity_date, '%Y-%m-%d') - datetime.datetime.now()).days + 1 From 5006ec9ca4f344baf52970860b1e3705a96bd142 Mon Sep 17 00:00:00 2001 From: scosist Date: Thu, 17 Mar 2016 14:04:51 -0600 Subject: [PATCH 11/14] [FIX] stock: typo in _get_orderpoint_date_planned orderpoint.lead_type should be 'supplier' instead of 'purchase' opw-687311 Closes #11389 --- addons/stock/procurement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index 6d1a045ece8..9c8a7c45dae 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -423,7 +423,7 @@ class procurement_order(osv.osv): def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None): days = orderpoint.lead_days or 0.0 - if orderpoint.lead_type=='purchase': + if orderpoint.lead_type == 'supplier': # These days will be substracted when creating the PO days += orderpoint.product_id._select_seller(orderpoint.product_id).delay or 0.0 date_planned = start_date + relativedelta(days=days) From 1d457006e53dcd62e4804b9572c3e747c6337de0 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 15 Sep 2016 15:37:50 +0200 Subject: [PATCH 12/14] [FIX] core: handle NewId in result of onchange When an onchange return a NewId for a many2one, the expected result is a False value. It appear for computed many2one that can return currently created record like `res.partner.commercial_partner_id` field. --- openerp/addons/test_new_api/models.py | 9 +++++++ .../test_new_api/tests/test_onchange.py | 27 +++++++++++++++++++ openerp/addons/test_new_api/views.xml | 1 + openerp/fields.py | 4 +++ 4 files changed, 41 insertions(+) diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index adcf3baac64..9ac3f9e2ea5 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -82,6 +82,7 @@ class Category(models.Model): name = fields.Char(required=True) color = fields.Integer('Color Index') parent = fields.Many2one('test_new_api.category') + root_categ = fields.Many2one(_name, compute='_compute_root_categ') display_name = fields.Char(compute='_compute_display_name', inverse='_inverse_display_name') dummy = fields.Char(store=False) discussions = fields.Many2many('test_new_api.discussion', 'test_new_api_discussion_category', @@ -95,6 +96,14 @@ class Category(models.Model): else: self.display_name = self.name + @api.depends('parent') + def _compute_root_categ(self): + for cat in self: + current = cat + while current.parent: + current = current.parent + cat.root_categ = current + @api.one def _inverse_display_name(self): names = self.display_name.split('/') diff --git a/openerp/addons/test_new_api/tests/test_onchange.py b/openerp/addons/test_new_api/tests/test_onchange.py index adecda3e4aa..8516f9394e2 100644 --- a/openerp/addons/test_new_api/tests/test_onchange.py +++ b/openerp/addons/test_new_api/tests/test_onchange.py @@ -75,6 +75,33 @@ class TestOnChange(common.TransactionCase): result = self.Message.onchange(values, 'body', field_onchange) self.assertNotIn('name', result['value']) + def test_onchange_many2one(self): + Category = self.env['test_new_api.category'] + + field_onchange = Category._onchange_spec() + self.assertEqual(field_onchange.get('parent'), '1') + + root = Category.create(dict(name='root')) + + values = { + 'name': 'test', + 'parent': root.id, + 'root_categ': False, + } + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertEqual(result['root_categ'], root.name_get()[0]) + + values.update(result) + values['parent'] = False + + self.env.invalidate_all() + result = Category.onchange(values, 'parent', field_onchange).get('value', {}) + self.assertIn('root_categ', result) + self.assertIs(result['root_categ'], False) + def test_onchange_one2many(self): """ test the effect of onchange() on one2many fields """ BODY = "What a beautiful day!" diff --git a/openerp/addons/test_new_api/views.xml b/openerp/addons/test_new_api/views.xml index 9cc5c6ce2ce..b8e56e1119c 100644 --- a/openerp/addons/test_new_api/views.xml +++ b/openerp/addons/test_new_api/views.xml @@ -207,6 +207,7 @@ + diff --git a/openerp/fields.py b/openerp/fields.py index b7795ec3e05..5d3fe58c57d 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1717,6 +1717,10 @@ class Many2one(_Relational): def convert_to_display_name(self, value, record=None): return ustr(value.display_name) + def convert_to_onchange(self, value, fnames=None): + if not value.id: + return False + return super(Many2one, self).convert_to_onchange(value, fnames) class UnionUpdate(SpecialValue): """ Placeholder for a value update; when this value is taken from the cache, From 5e6df4280076883c1390b36673dad8ae4b1b2913 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Thu, 15 Sep 2016 15:38:36 +0200 Subject: [PATCH 13/14] [FIX] web_editor: use valid bootstrap for m2o editor options The .dropdown is in a .btn-group environment while a .btn-group can only contain .btn or .btn-group elements. This induced problems where the m2o options covers the full overlay environment, preventing clicks on the customize dropdown. --- addons/web_editor/static/src/xml/snippets.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml index 0b194de6af9..654c5e9fcd1 100644 --- a/addons/web_editor/static/src/xml/snippets.xml +++ b/addons/web_editor/static/src/xml/snippets.xml @@ -60,7 +60,7 @@ -