From 87b42ad9a887faacbbefcab9dd0703a5c51ce28b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 18 Apr 2018 10:48:40 +0200 Subject: [PATCH 01/65] [FIX] models: make group by date and order by date work together The issue occurs when `read_group` is called with a date/datetime field to group and order on, and the group_by is qualified, such as: model.read_group(..., groupby=['date:week'], orderby='date') The ORDER BY clause in the query should use the same term as the GROUP BY clause for the corresponding field. OPW 1834148 --- odoo/addons/base/tests/test_orm.py | 19 +++++++++++++++++++ odoo/models.py | 17 ++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/odoo/addons/base/tests/test_orm.py b/odoo/addons/base/tests/test_orm.py index ef586218a0d78..7f39097b2ddb7 100644 --- a/odoo/addons/base/tests/test_orm.py +++ b/odoo/addons/base/tests/test_orm.py @@ -163,6 +163,25 @@ def read_group(interval): ['date:month', 'date:day'], lazy=False) self.assertEqual(len(res), len(partner_ids)) + # combine groupby and orderby + months = ['February 2013', 'January 2013', 'December 2012', 'November 2012'] + res = partners.read_group([('id', 'in', partner_ids)], ['date'], + groupby=['date:month'], orderby='date:month DESC') + self.assertEqual([item['date:month'] for item in res], months) + + # order by date should reorder by date:month + res = partners.read_group([('id', 'in', partner_ids)], ['date'], + groupby=['date:month'], orderby='date DESC') + self.assertEqual([item['date:month'] for item in res], months) + + # order by date should reorder by date:day + days = ['11 Feb 2013', '28 Jan 2013', '14 Jan 2013', '07 Jan 2013', + '31 Dec 2012', '17 Dec 2012', '19 Nov 2012'] + res = partners.read_group([('id', 'in', partner_ids)], ['date'], + groupby=['date:month', 'date:day'], + orderby='date DESC', lazy=False) + self.assertEqual([item['date:day'] for item in res], days) + def test_write_duplicate(self): p1 = self.env['res.partner'].create({'name': 'W'}) (p1 + p1).write({'name': 'X'}) diff --git a/odoo/models.py b/odoo/models.py index a47fd56b0d794..f5c8a84595d90 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1735,31 +1735,38 @@ def _read_group_prepare(self, orderby, aggregated_fields, annotated_groupbys, qu """ orderby_terms = [] groupby_terms = [gb['qualified_field'] for gb in annotated_groupbys] - groupby_fields = [gb['groupby'] for gb in annotated_groupbys] if not orderby: return groupby_terms, orderby_terms self._check_qorder(orderby) + + # when a field is grouped as 'foo:bar', both orderby='foo' and + # orderby='foo:bar' generate the clause 'ORDER BY "foo:bar"' + groupby_fields = { + gb[key]: gb['groupby'] + for gb in annotated_groupbys + for key in ('field', 'groupby') + } for order_part in orderby.split(','): order_split = order_part.split() order_field = order_split[0] if order_field == 'id' or order_field in groupby_fields: - if self._fields[order_field.split(':')[0]].type == 'many2one': order_clause = self._generate_order_by(order_part, query).replace('ORDER BY ', '') if order_clause: orderby_terms.append(order_clause) groupby_terms += [order_term.split()[0] for order_term in order_clause.split(',')] else: - order = '"%s" %s' % (order_field, '' if len(order_split) == 1 else order_split[1]) - orderby_terms.append(order) + order_split[0] = '"%s"' % groupby_fields.get(order_field, order_field) + orderby_terms.append(' '.join(order_split)) elif order_field in aggregated_fields: - order_split[0] = '"' + order_field + '"' + order_split[0] = '"%s"' % order_field orderby_terms.append(' '.join(order_split)) else: # Cannot order by a field that will not appear in the results (needs to be grouped or aggregated) _logger.warn('%s: read_group order by `%s` ignored, cannot sort on empty columns (not grouped/aggregated)', self._name, order_part) + return groupby_terms, orderby_terms @api.model From 23431389c36e57bfce99afb97abfca6a062cfc74 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 17 Apr 2018 16:57:39 +0200 Subject: [PATCH 02/65] [FIX] models: bad setup of inherited custom fields Assume a custom field F is defined on model 'res.partner'. The setup of F may silently fail because of missing stuff. In that situation, setting up the field inherited from F on model 'res.users' should also silently fail. To reproduce the bug, install Invoicing, create a related custom field F on 'res.partner' with 'property_account_position_id.active', and install another module. Setting up F after loading module 'base' will fail because the field 'property_account_position_id' does not exist yet. The error is not caught by the inheritance of F on model 'res.users', and the installation crashes. OPW 1835872 --- odoo/fields.py | 3 ++- odoo/models.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/odoo/fields.py b/odoo/fields.py index 6004391592523..b472e35658bdc 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -320,6 +320,7 @@ class Second(models.Model): 'automatic': False, # whether the field is automatically created ("magic" field) 'inherited': False, # whether the field is inherited (_inherits) + 'inherited_field': None, # the corresponding inherited field 'name': None, # name of the field 'model_name': None, # name of the model of this field @@ -647,7 +648,7 @@ def _search_related(self, records, operator, value): @property def base_field(self): """ Return the base field of an inherited field, or ``self``. """ - return self.related_field.base_field if self.inherited else self + return self.inherited_field.base_field if self.inherited_field else self # # Company-dependent fields diff --git a/odoo/models.py b/odoo/models.py index f5c8a84595d90..d04e6642254ff 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2734,6 +2734,7 @@ def _add_inherited_fields(self): # - copy inherited fields iff their original field is copied fields[name] = field.new( inherited=True, + inherited_field=field, related=(parent_field, name), related_sudo=False, copy=field.copy, @@ -2850,7 +2851,7 @@ def _setup_fields(self, partial): try: field.setup_full(self) except Exception: - if partial and field.manual: + if partial and field.base_field.manual: # Something goes wrong when setup a manual field. # This can happen with related fields using another manual many2one field # that hasn't been loaded because the comodel does not exist yet. From c9caf55177359b404e4d752dcf1966ca58dccfda Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 17 Apr 2018 16:57:39 +0200 Subject: [PATCH 03/65] [FIX] models: bad setup of inherited custom fields Assume a custom field F is defined on model 'res.partner'. The setup of F may silently fail because of missing stuff. In that situation, setting up the field inherited from F on model 'res.users' should also silently fail. To reproduce the bug, install Invoicing, create a related custom field F on 'res.partner' with 'property_account_position_id.active', and install another module. Setting up F after loading module 'base' will fail because the field 'property_account_position_id' does not exist yet. The error is not caught by the inheritance of F on model 'res.users', and the installation crashes. --- odoo/fields.py | 3 ++- odoo/models.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/odoo/fields.py b/odoo/fields.py index caa36df49ef3f..a92d814d2bb88 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -277,6 +277,7 @@ class Second(models.Model): 'automatic': False, # whether the field is automatically created ("magic" field) 'inherited': False, # whether the field is inherited (_inherits) + 'inherited_field': None, # the corresponding inherited field 'name': None, # name of the field 'model_name': None, # name of the model of this field @@ -611,7 +612,7 @@ def _search_related(self, records, operator, value): @property def base_field(self): """ Return the base field of an inherited field, or ``self``. """ - return self.related_field.base_field if self.inherited else self + return self.inherited_field.base_field if self.inherited_field else self # # Company-dependent fields diff --git a/odoo/models.py b/odoo/models.py index 74b4431b51f5f..a9670ca2257a6 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2292,6 +2292,7 @@ def _add_inherited_fields(self): # - copy inherited fields iff their original field is copied fields[name] = field.new( inherited=True, + inherited_field=field, related=(parent_field, name), related_sudo=False, copy=field.copy, @@ -2409,7 +2410,7 @@ def _setup_fields(self): try: field.setup_full(self) except Exception: - if not self.pool.loaded and field.manual: + if not self.pool.loaded and field.base_field.manual: # Something goes wrong when setup a manual field. # This can happen with related fields using another manual many2one field # that hasn't been loaded because the comodel does not exist yet. From 386c91b8563369997f87423bac682b2c3ca8150b Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 19 Apr 2018 08:39:31 +0200 Subject: [PATCH 04/65] [FIX] account: missing widget A Many2many field is unusable in a list view without the proper widget... opw-1837903 --- addons/account/views/account_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 870af2672399c..16ed285ea510c 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -1466,7 +1466,7 @@ domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/> - + From dccef8e41d45dd5150e4112aa3a2b19ba698dc62 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 18 Apr 2018 10:03:00 +0200 Subject: [PATCH 05/65] [FIX] mail: auto-subscribe at user creation Auto-subscribe to channels at user creation. opw-1834031 --- addons/mail/models/res_users.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/mail/models/res_users.py b/addons/mail/models/res_users.py index c5b2d9bbec847..36acb6895bc44 100644 --- a/addons/mail/models/res_users.py +++ b/addons/mail/models/res_users.py @@ -58,6 +58,8 @@ def create(self, values): # create a welcome message user._create_welcome_message() + # Auto-subscribe to channels + self.env['mail.channel'].search([('group_ids', 'in', user.groups_id.ids)])._subscribe_users() return user @api.multi From 1d1fd8219cd3c328c54e71c055527e31fb598848 Mon Sep 17 00:00:00 2001 From: asa-odoo Date: Fri, 9 Mar 2018 18:35:55 +0530 Subject: [PATCH 06/65] [FIX] website: fix design of mobile preview close icon The broken design prevented to click on the close button. task-1817880 --- addons/website/static/src/less/website.ui.components.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website/static/src/less/website.ui.components.less b/addons/website/static/src/less/website.ui.components.less index 52ee0f4d7912d..f708ce5d2949d 100644 --- a/addons/website/static/src/less/website.ui.components.less +++ b/addons/website/static/src/less/website.ui.components.less @@ -44,7 +44,7 @@ body .modal { .o-w-preserve-modals(); .o-w-preserve-tabs(); - button.close { + &:not(.oe_mobile_preview) button.close { .o-w-close-icon(12px); margin-right: -2px; } From ce61f388c418288e0e7a0c5bedc6649305bb79e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Snauwaert?= Date: Thu, 19 Apr 2018 14:03:06 +0200 Subject: [PATCH 07/65] [FIX] account: fix domain in manual reconciliation Previsouly, the m2o fields in writeoff creation mode of manual reconciliation widget were always empty because of a bad domain filtering out all the existing records (company_id was unknown). --- addons/account/models/reconciliation_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account/models/reconciliation_widget.py b/addons/account/models/reconciliation_widget.py index 9f1f4c5addd21..af59be29d4f49 100644 --- a/addons/account/models/reconciliation_widget.py +++ b/addons/account/models/reconciliation_widget.py @@ -338,6 +338,7 @@ def get_data_for_manual_reconciliation(self, res_type, res_ids=None, account_typ partner_id = is_partner and row['partner_id'] or None rec_prop = self._get_move_line_reconciliation_proposition(account.id, partner_id) row['reconciliation_proposition'] = self._prepare_move_lines(rec_prop, target_currency=currency) + row['company_id'] = account.company_id.id return rows @api.model From 6c97aeee8de0ba495d48e659c9e50a5b4ca33d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Thu, 19 Apr 2018 14:11:40 +0200 Subject: [PATCH 08/65] [FIX] web: use FieldText as fallback for html fields Currently, the default field widget of type html is not defined in the web addon, but in the web_editor addon. It uses the summernote library and different assets. The web_editor addon is defined with auto_install=true, so it is usually not an issue. However, it could happen that there is some issue with the web_editor asset bundles. In that case, any form view with a field of type html will crash. With this commit, we define a default widget of type html in the web addon. This will render as text (so, not exactly what one might expect), but it is still better than making the web client unusable. --- addons/web/static/src/js/fields/field_registry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 6692663d5f90f..fb7dc4321546b 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -26,6 +26,7 @@ registry .add('datetime', basic_fields.FieldDateTime) .add('domain', basic_fields.FieldDomain) .add('text', basic_fields.FieldText) + .add('html', basic_fields.FieldText) .add('float', basic_fields.FieldFloat) .add('char', basic_fields.FieldChar) .add('link_button', basic_fields.LinkButton) From b8bf945335f2e52dbd07f2560108f2ee4637598d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Thu, 19 Apr 2018 14:34:00 +0200 Subject: [PATCH 09/65] [FIX] web: do not change pager when discarding changes Here is a scenario that could happen before this commit: 1. go to a list view with more than one record, say 3 2. click on the 3rd record to open the form view 3. the pager says 3/3 4. click on edit button 5. click on discard button 6. the pager says 1/3 (but the record displayed is still the same) It is often necessary to restore the offset for the sub records, because the number of pages in a one2many could have changed. However, it is not a good idea to do that for the main record, since it interferes with the usual flow of operations. --- .../static/src/js/views/basic/basic_model.js | 2 ++ addons/web/static/tests/views/form_tests.js | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 8fdcb305ddc6f..969e81576b1bb 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -347,6 +347,7 @@ var BasicModel = AbstractModel.extend({ var element = this.localData[id]; var isNew = this.isNew(id); var rollback = 'rollback' in options ? options.rollback : isNew; + var initialOffset = element.offset; this._visitChildren(element, function (elem) { if (rollback && elem._savePoint) { if (elem._savePoint instanceof Array) { @@ -365,6 +366,7 @@ var BasicModel = AbstractModel.extend({ delete elem.tempLimitIncrement; } }); + element.offset = initialOffset; }, /** * Duplicate a record (by calling the 'copy' route) diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 8acae55b91527..dfd58db57c2f0 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -6525,5 +6525,35 @@ QUnit.module('Views', { testUtils.unpatch(mixins.ParentedMixin); }); + QUnit.test('do not change pager when discarding current record', function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '', + viewOptions: { + ids: [1, 2], + index: 0, + }, + res_id: 2, + }); + + assert.strictEqual(form.pager.$('.o_pager_counter').text().trim(), '2 / 2', + 'pager should indicate that we are on second record'); + + form.$buttons.find('.o_form_button_edit').click(); + form.$buttons.find('.o_form_button_cancel').click(); + + assert.strictEqual(form.pager.$('.o_pager_counter').text().trim(), '2 / 2', + 'pager should not have changed'); + + form.destroy(); + }); + + }); }); From dea07fc29116c36c8a3773eec0fce526a972ba08 Mon Sep 17 00:00:00 2001 From: XavierDo Date: Tue, 20 Mar 2018 12:48:44 +0100 Subject: [PATCH 10/65] [IMP] project, portal: group tasks by project on portal. The portal view for tasks previously listed all task of all projects without grouping them. It was a problem, mainly when we want to make the distinction on project label_tasks, to make the difference between tasks, issues, ... Grouping task by project by default allows to show the label_tasks before each project. Grouping the tasks removes the possibility to define a primary ordering. We want to be able to ungroup the tasks in some cases. The implentation was done in the portalsearch bar adding a group by option. The portal proposes two grouping options, project (by default) and none (to keep strict ordering). Task: #1827950 PR #23769 --- addons/portal/views/portal_templates.xml | 15 +++++ addons/project/controllers/portal.py | 18 +++++- .../views/project_portal_templates.xml | 57 ++++++++++--------- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/addons/portal/views/portal_templates.xml b/addons/portal/views/portal_templates.xml index c02573f761386..fef256890aedd 100644 --- a/addons/portal/views/portal_templates.xml +++ b/addons/portal/views/portal_templates.xml @@ -195,6 +195,21 @@ + +