From 7c959e3fd55d8faab1c0a9ea0e32fc0fdb975165 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 26 Jan 2018 14:29:27 +0100 Subject: [PATCH 01/13] [FIX] web_editor: properly show link alignment in toolbar When clicking on a link in the editor, the "center" alignment button was always marked as active whatever the real alignment of the link. This was because it considered the alignment of the text inside the link instead of the link's one inside its parent. --- addons/web_editor/static/src/js/summernote.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js index cdf4329c0b9..da837d67354 100644 --- a/addons/web_editor/static/src/js/summernote.js +++ b/addons/web_editor/static/src/js/summernote.js @@ -2015,6 +2015,14 @@ eventHandler.modules.editor.currentStyle = function(target) { if(r) styleInfo.image = r.isOnImg(); } + // Fix when the target is a link: the text-align buttons state should + // indicate the alignment of the link in the parent, not the text inside + // the link (which is not possible to customize with summernote). Summernote fixed + // this in their newest version... by just not showing the active button + // for alignments. + if (styleInfo.anchor) { + styleInfo['text-align'] = $(styleInfo.anchor).parent().css('text-align'); + } return styleInfo; } From aae4d8a013e980f8c211d3a5a0249efdf0e628b3 Mon Sep 17 00:00:00 2001 From: Adrian Torres Date: Mon, 29 Jan 2018 10:59:32 +0100 Subject: [PATCH 02/13] [FIX][IMP] doc: document manifest features (#22474) Some manifest features, such as external_dependencies, hooks, etc. were not previously documented, this commit documents these features. Closes #22471 --- doc/reference/module.rst | 45 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/doc/reference/module.rst b/doc/reference/module.rst index d4ce37a1862..c145448d6cc 100644 --- a/doc/reference/module.rst +++ b/doc/reference/module.rst @@ -1,5 +1,3 @@ -:banner: banners/module.jpg - ======= Modules ======= @@ -84,6 +82,49 @@ Available manifest fields are: to ``auto_install``. When both ``sale`` and ``crm`` are installed, it automatically adds CRM campaigns tracking to sale orders without either ``sale`` or ``crm`` being aware of one another +``external_dependencies`` (``dict(key=list(str))``) + A dictionary containing python and/or binary dependencies. + + For python dependencies, the ``python`` key must be defined for this + dictionary and a list of python modules to be imported should be assigned + to it. + + For binary dependencies, the ``bin`` key must be defined for this + dictionary and a list of binary executable names should be assigned to it. + + The module won't be installed if either the python module is not installed + in the host machine or the binary executable is not found within the + host machine's PATH environment variable. +``application`` (``bool``, default: ``False``) + Whether the module should be considered as a fully-fledged application + (``True``) or is just a technical module (``False``) that provides some + extra functionality to an existing application module. +``css`` (``list(str)``) + Specify css files with custom rules to be imported, these files should be + located in ``static/src/css`` inside the module. +``images`` (``list(str)``) + Specify image files to be used by the module. +``installable`` (``bool`` default: ``False``) + Whether a user should be able to install the module from the Web UI or not. +``maintainer`` (``str``) + Person or entity in charge of the maintenance of this module, by default + it is assumed that the author is the maintainer. +``{pre_init, post_init, uninstall}_hook`` (``str``) + Hooks for module installation/uninstallation, their value should be a + string representing the name of a function defined inside the module's + ``__init__.py``. + + ``pre_init_hook`` takes a cursor as its only argument, this function is + executed prior to the module's installation. + + ``post_init_hook`` takes a cursor and a registry as its arguments, this + function is executed right after the module's installation. + + ``uninstall_hook`` takes a cursor and a registry as its arguments, this + function is executed after the module's uninstallation. + + These hooks should only be used when setup/cleanup required for this module + is either extremely difficult or impossible through the api. .. _semantic versioning: http://semver.org .. _existing categories: From 5b230c34cb960f3f729d6612735dd9c3c896b785 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Mon, 29 Jan 2018 10:29:12 +0100 Subject: [PATCH 03/13] [FIX] web: noshow list view aggregates w/ currency In df69b5bb the aggregates of a column with currency was shown only if records were of the same currency when a grouping was closed. But when the list view is grouped, this would cause a number of issue: - error if with first grouping is opened, another one is closed - if the first grouping was closed aggregates were always shown - the code only acts when closing a grouping - aggregates could be shown when they should not - aggregates could be not shown when they should So this commit change things so this feature is ignored when the list view is grouped. There is also a fix so `currency_field` of the column is used instead of just using `currency_id` that may be wrong. note: only for saas-14 and saas-15 (feature is not present in saas-16) opw-810835 closes #22598 --- addons/web/static/src/js/views/list_view.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js index 83e1b7bf900..d28ac5e0774 100644 --- a/addons/web/static/src/js/views/list_view.js +++ b/addons/web/static/src/js/views/list_view.js @@ -713,6 +713,7 @@ var ListView = View.extend({ * @param {Array} [records] */ compute_aggregates: function (records) { + var self = this; var columns = _(this.aggregate_columns).filter(function (column) { return column['function']; }); if (_.isEmpty(columns)) { return; } @@ -763,8 +764,12 @@ var ListView = View.extend({ var aggregates = {}; _.each(_.filter(columns, function (column) { - if (column.currency_field && records.length > 0 && records[0].values['currency_id']) { - var currency_ids = _.map(records, function(record) {return record.values['currency_id'][0]}); + if (!self.grouped && column.currency_field) { + var currency_ids = _.map(records, function(record) { + if (record.values[column.currency_field]) { + return record.values[column.currency_field][0]; + } + }); if (_.every(currency_ids, function (currency_id){return currency_id === currency_ids[0]})) { return column; } From 5c5098f74070b2f312f4b9d1418971e6808ff335 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 20 Dec 2017 18:03:44 +0100 Subject: [PATCH 04/13] [FIX] stock: performance issue inventory at date On a production database with ~10000 products and multiple stock locations, uunning the 'Inventory at date' tales ~50 minutes. Most of the time is spent in the loop populating `group_lines` thanks to multiple search queries. We get rid of this loop by selecting the stock history lines and making the match between these lines and the `read_group` result manually. On the reference database, the duration falls to ~30 seconds. Based on preliminary work by amoyaux opw-803460 --- addons/stock_account/report/stock_history.py | 40 ++++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/addons/stock_account/report/stock_history.py b/addons/stock_account/report/stock_history.py index 94f2c5f36a3..ee5a5554729 100644 --- a/addons/stock_account/report/stock_history.py +++ b/addons/stock_account/report/stock_history.py @@ -25,22 +25,35 @@ class StockHistory(models.Model): @api.model def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): + # Step 1: retrieve the standard read_group output. In case of inventory valuation, this + # will be mostly used as a 'skeleton' since the inventory value needs to be computed based + # on the individual lines. res = super(StockHistory, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) if 'inventory_value' in fields: + groupby_list = groupby if not lazy else groupby[:-1] date = self._context.get('history_date', fieldsDatetime.now()) - stock_history = self.env['stock.history'] - group_lines = {} - for line in res: - domain = line.get('__domain', domain) - group_lines.setdefault(str(domain), self.search(domain)) - stock_history |= group_lines[str(domain)] - # get data of stock_history in one shot to speed things up (the view can be very slow) + # Step 2: retrieve the stock history lines. The result contains the 'expanded' + # version of the read_group. We build the query manually for performance reason + # (and avoid a costly 'WHERE id IN ...'). + fields_2 = set( + ['id', 'product_id', 'price_unit_on_quant', 'company_id', 'quantity'] + groupby_list + ) + tables, where_clause, where_clause_params = self._where_calc(domain).get_sql() + select = "SELECT %s FROM %s WHERE %s " + query = select % (','.join(fields_2), tables, where_clause) + self._cr.execute(query, where_clause_params) + + # Step 3: match the lines retrieved at step 2 with the aggregated results of step 1. + # In other words, we link each item of the read_group result with the corresponding + # lines. stock_history_data = {} - if stock_history: - self._cr.execute("""SELECT id, product_id, price_unit_on_quant, company_id, quantity - FROM stock_history WHERE id in %s""", (tuple(stock_history.ids),)) - stock_history_data = {line['id']: line for line in self._cr.dictfetchall()} + stock_histories_by_group = {} + for line in self._cr.dictfetchall(): + stock_history_data[line['id']] = line + key = tuple(line[g] if g in line else False for g in groupby_list) + stock_histories_by_group.setdefault(key, []) + stock_histories_by_group[key] += [line['id']] histories_dict = {} not_real_cost_method_products = self.env['product.product'].browse( @@ -56,7 +69,10 @@ class StockHistory(models.Model): for line in res: inv_value = 0.0 - for stock_history in group_lines.get(str(line.get('__domain', domain))): + # Build the same keys than above, but need to take into account Many2one are tuples + key = tuple(line[g] if g in line else False for g in groupby_list) + key = tuple(k[0] if isinstance(k, tuple) else k for k in key) + for stock_history in self.env['stock.history'].browse(stock_histories_by_group[key]): history_data = stock_history_data[stock_history.id] product_id = history_data['product_id'] if self.env['product.product'].browse(product_id).cost_method == 'real': From 110613b2ccea313939f6ed6afe15ad6821fc1217 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Mon, 29 Jan 2018 12:28:23 +0100 Subject: [PATCH 05/13] [FIX] l10n_in_hr_payroll: Error printing payslip details Programming error opw:803169 --- .../views/report_payslip_details_template.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/l10n_in_hr_payroll/views/report_payslip_details_template.xml b/addons/l10n_in_hr_payroll/views/report_payslip_details_template.xml index eacbaa1533b..a8165b4420a 100644 --- a/addons/l10n_in_hr_payroll/views/report_payslip_details_template.xml +++ b/addons/l10n_in_hr_payroll/views/report_payslip_details_template.xml @@ -62,7 +62,8 @@ - + @@ -75,7 +76,8 @@ - + From 93c4c5cfb1945be6afdb3367b9f5b73f7a2c4f89 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 30 Jan 2018 13:59:45 +0100 Subject: [PATCH 06/13] [FIX] mrp: multi-company bom find - Create 2 companies A & B - Create a product P with 2 phantom BOMs: Component C1 for company A, with sequence = 10 Component C2 for company B, with sequence = 0 - Create a SO in company A for P, validate The picking contains C2, while one would expect C1 since C2 is only for company B. opw-807640 --- addons/mrp/models/stock_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 661de7c93a9..c831f49d63d 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -359,7 +359,7 @@ class StockMove(models.Model): # all grouped in the same picking. if not self.picking_type_id: return self - bom = self.env['mrp.bom'].sudo()._bom_find(product=self.product_id) + bom = self.env['mrp.bom'].sudo()._bom_find(product=self.product_id, company_id=self.company_id.id) if not bom or bom.type != 'phantom': return self phantom_moves = self.env['stock.move'] From 8c941cbfb16ab914ebf635b0d2a56a3da6c2e20a Mon Sep 17 00:00:00 2001 From: moutio Date: Wed, 31 Jan 2018 09:56:58 +0100 Subject: [PATCH 07/13] [CLA] signature for MouTio Done at #22605 --- doc/cla/individual/moutio.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/cla/individual/moutio.md diff --git a/doc/cla/individual/moutio.md b/doc/cla/individual/moutio.md new file mode 100644 index 00000000000..4a1b5b4b955 --- /dev/null +++ b/doc/cla/individual/moutio.md @@ -0,0 +1,9 @@ +Spain, 2018-01-29 + +I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this declaration. + +Signed, + +Nacho Muñoz nacmuro@gmail.com https://github.com/moutio From 9dce2be2735b57d9c6781fbdd43725ab5f32cc5a Mon Sep 17 00:00:00 2001 From: adekock11 Date: Tue, 30 Jan 2018 12:15:37 +0200 Subject: [PATCH 08/13] [FIX] mail: display notification when explicitly grant it Instead of looking for the "denied" permission, rather look for the "granted" since there are three permissions: "default", "denied", "granted" - only "granted" counts toward permission actually being granted Closes #22633 --- addons/mail/static/src/js/client_action.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index 165dda719c2..cd8889a900e 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -124,10 +124,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, { var def = window.Notification && window.Notification.requestPermission(); if (def) { def.then(function (value) { - if (value === 'denied') { - utils.send_notification(_t('Permission denied'), _t('Odoo will not have the permission to send native notifications on this device.')); - } else { + if (value === 'granted') { utils.send_notification(_t('Permission granted'), _t('Odoo has now the permission to send you native notifications on this device.')); + } else { + utils.send_notification(_t('Permission denied'), _t('Odoo will not have the permission to send native notifications on this device.')); } }); } From d295b4360db93a0d3333153098e623e573ac099b Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Tue, 30 Jan 2018 13:17:50 +0100 Subject: [PATCH 09/13] [FIX] website_sale: properly switch images when selecting a variant When using product variants, the product page shows: - The product variant if different from the product image - The product image - Extra product images The product variant image has to be switched dynamically when the user choose another variant. This was broken with commit https://github.com/odoo/odoo/commit/f98bb2b86bb7c3ff0a549f24b31616c1539420a7 Indeed, it was switching the product image with the product image variant instead. This was because the variant/template order changed without switching their classes. This commit changes their order again. Also see opw-784737 --- addons/website_sale/views/templates.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 4bd9782cfb8..1adb21f370e 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -405,8 +405,8 @@