From efa66eb83b9ddd5f5420b92d73ccfdf87f1111aa Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 14 Sep 2016 12:13:37 +0200 Subject: [PATCH 01/31] [FIX] tx: correct the path to base.pot In saas-12, it is still openerp, changed in saas-13 --- .tx/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tx/config b/.tx/config index 0abdd5345bf..a4217380940 100644 --- a/.tx/config +++ b/.tx/config @@ -3,8 +3,8 @@ host = https://www.transifex.com type = PO [odoo-master.base] -file_filter = odoo/addons/base/i18n/.po -source_file = odoo/addons/base/i18n/base.pot +file_filter = openerp/addons/base/i18n/.po +source_file = openerp/addons/base/i18n/base.pot source_lang = en [odoo-master.account] From 01c68bc2a4bed263dd5ff298825e1dc25bcc2fe3 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 14 Sep 2016 12:44:54 +0200 Subject: [PATCH 02/31] [FIX] website_sale: fix variants availability on website product page This is an oversight during the conversion to the new API in the revision 788c1334e2cbe26fa26d4c6d4974dc413dbff608 In the above revision, ``` visible_attrs = set(l.attribute_id.id for l in product.attribute_line_ids if len(l.value_ids) > 1) ``` has been converted to ``` visible_attrs_ids = product.mapped('attribute_line_ids.attribute_id').filtered(lambda attr: len(attr.value_ids) > 1).ids ``` which leads to a change of behavior: Before the conversion, the filter is applied on the attribute line `value_ids` field: `if len(l.value_ids) > 1`, `l` being one item of `attribute_line_ids` After the converison, the filter is applied on the attribute line `attribute_id.value_ids` field `filtered(lambda attr: len(attr.value_ids) > 1)`, `attr` being the `attribute_id` field of one item of `attribute_line_ids` Therefore breaking the availability of the product variants in the products pages of the ecommerce. e.g. Attribute color, values red, blue, white, black The product "Tshirt" is defined with as possible colors values red only. Before, the filter would not return the attribute `color` for this product, as the product has only one possible value for this attribute After, the filter would return the attribute `color`, since this attribute as more than one possible values (but, not for this product) opw-688470 --- addons/website_sale/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index bc367eb917b..e6124ca0291 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -110,7 +110,7 @@ class WebsiteSale(http.Controller): (variant id, [visible attribute ids], variant price, variant sale price) """ # product attributes with at least two choices - visible_attrs_ids = product.mapped('attribute_line_ids.attribute_id').filtered(lambda attr: len(attr.value_ids) > 1).ids + visible_attrs_ids = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id').ids to_currency = request.website.get_current_pricelist().currency_id attribute_value_ids = [] for variant in product.product_variant_ids: From 9fdbe3499a08a048df0684118f89f9372973c772 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 14 Sep 2016 12:54:15 +0200 Subject: [PATCH 03/31] [FIX] website_sale: fix product website description translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an oversight during the conversion to the new API in the revision 788c1334e2cbe26fa26d4c6d4974dc413dbff608 `html_translate` allows to split the source term in several pieces, so the translations can be done pieces by pieces. For HTML fields, this is the default method of translation, EXCEPT if `sanitize´ is set to `False`. Unfortunately, this is the case for this field, and therefore `html_translate` must still be precised for this field, while, for other HTML fields, not having `sanitize` set to `False`, `translate=True` is enough to use the method `html_translate` opw-688470 --- addons/website_sale/models/product.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index f429b0930d1..5c896fd956d 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, tools, _ +from odoo.tools.translate import html_translate class ProductStyle(models.Model): @@ -80,7 +81,7 @@ class ProductTemplate(models.Model): domain=lambda self: ['&', ('model', '=', self._name), ('message_type', '=', 'comment')], string='Website Comments', ) - website_description = fields.Html('Description for the website', sanitize=False, translate=True) + website_description = fields.Html('Description for the website', sanitize=False, translate=html_translate) alternative_product_ids = fields.Many2many('product.template', 'product_alternative_rel', 'src_id', 'dest_id', string='Suggested Products', help='Appear on the product page') accessory_product_ids = fields.Many2many('product.product', 'product_accessory_rel', 'src_id', 'dest_id', From 54968d55c243d9890804305915ee72b6fe44c2ae Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 14 Sep 2016 13:04:49 +0200 Subject: [PATCH 04/31] [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 1867ba03d63a9e9994c0b46f3a5e9247cbd1d547 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 14 Sep 2016 13:58:46 +0200 Subject: [PATCH 05/31] [FIX] base: incorrect update An error arises when adding a rule to a group (Settings > Groups > Rules): "Mixing apples and..." The issue comes from an oversight of commit 1ec7b28a09, which modifies the value of the parameter `value`, while it should not. opw-688434 --- openerp/fields.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index e48597542df..64d90b047da 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1774,10 +1774,10 @@ class _RelationalMulti(_Relational): """ Update the cached value of ``self`` for ``records`` with ``value``. """ for record in records: if self in record._cache: - value = self.convert_to_cache(record[self.name] | value, record, validate=False) + val = self.convert_to_cache(record[self.name] | value, record, validate=False) else: - value = UnionUpdate(self, record, value) - record._cache[self] = value + val = UnionUpdate(self, record, value) + record._cache[self] = val def convert_to_cache(self, value, record, validate=True): # cache format: tuple(ids) From 8914e7f48c84c891f055e44fec5ca4c7f23329c7 Mon Sep 17 00:00:00 2001 From: Joren Van Onder Date: Wed, 24 Aug 2016 12:07:04 +0200 Subject: [PATCH 06/31] [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 07/31] [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 08/31] [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 d614356965b04fff56a0fe176f78b198c3827562 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 14 Sep 2016 16:53:03 +0200 Subject: [PATCH 09/31] [FIX] project_issue: correct issue count on partners Also adapt default search to have the same domain --- addons/project_issue/models/res_partner.py | 3 +-- addons/project_issue/views/res_partner_view.xml | 2 +- openerp/addons/base/res/res_partner_view.xml | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/project_issue/models/res_partner.py b/addons/project_issue/models/res_partner.py index 8bfe514987f..91dd0af3eec 100644 --- a/addons/project_issue/models/res_partner.py +++ b/addons/project_issue/models/res_partner.py @@ -11,6 +11,5 @@ class ResPartner(models.Model): def _compute_issue_count(self): Issue = self.env['project.issue'] - partners = {id: self.search([('id', 'child_of', self.ids)]).ids for id in self.ids} for partner in self: - partner.issue_count = Issue.search_count([('partner_id', 'in', partners[partner.id])]) + partner.issue_count = Issue.search_count([('partner_id', 'child_of', partner.commercial_partner_id.id)]) diff --git a/addons/project_issue/views/res_partner_view.xml b/addons/project_issue/views/res_partner_view.xml index aa2ac8a80b5..10aafb7d24f 100644 --- a/addons/project_issue/views/res_partner_view.xml +++ b/addons/project_issue/views/res_partner_view.xml @@ -10,7 +10,7 @@
Date: Tue, 13 Sep 2016 15:20:49 +0200 Subject: [PATCH 19/31] [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 20/31] [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 21/31] [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 a2946f72d9e0e26d6e10e9bab8e1d3a09e270e6b Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 15 Sep 2016 11:44:37 +0200 Subject: [PATCH 22/31] [FIX] web: error when click twice on "Salary Structures Hierarchy" --- addons/web/static/src/js/views/tree_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views/tree_view.js b/addons/web/static/src/js/views/tree_view.js index aeb206257fe..604c9204b42 100644 --- a/addons/web/static/src/js/views/tree_view.js +++ b/addons/web/static/src/js/views/tree_view.js @@ -49,7 +49,7 @@ var TreeView = View.extend({ }, willStart: function () { _(this.fields_view.arch.children).each(function (field) { - if (field.attrs.modifiers) { + if (field.attrs.modifiers && typeof field.attrs.modifiers === "string") { field.attrs.modifiers = JSON.parse(field.attrs.modifiers); } }); From 5a104fcb1a281e8bd5ab78be69437102188bda9c Mon Sep 17 00:00:00 2001 From: Mansi Gandhi Date: Tue, 28 Jun 2016 10:38:40 +0530 Subject: [PATCH 23/31] [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 f83b5a8e560cd70493dd5c135d9538e0c6a83083 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 14 Sep 2016 15:34:21 +0200 Subject: [PATCH 24/31] [FIX] survey: search by title It's not possible to search on any field on the `survey.survey` model. Due to commit ed589ca. opw-688157 --- addons/survey/views/survey_views.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/survey/views/survey_views.xml b/addons/survey/views/survey_views.xml index 7aaa3706766..a01208572ad 100644 --- a/addons/survey/views/survey_views.xml +++ b/addons/survey/views/survey_views.xml @@ -231,6 +231,7 @@ survey.survey + From 5006ec9ca4f344baf52970860b1e3705a96bd142 Mon Sep 17 00:00:00 2001 From: scosist Date: Thu, 17 Mar 2016 14:04:51 -0600 Subject: [PATCH 25/31] [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 26/31] [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 27/31] [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 @@ -