From 2adc9d50321d58fa7f33a0231a5594d338a3ffa4 Mon Sep 17 00:00:00 2001 From: Jigar Patel Date: Tue, 9 May 2017 15:02:17 +0530 Subject: [PATCH] [IMP] account: Add a new boolean_favorite widget Purpose ======= Projects and soon sales teams have a manual "favorite" button that calls a toggle_favorite method It should actually be a widget on a boolean field Specifications ============== Develop a favorite widget for kanban view that allows to toggle a boolean field Probably update boolean field to allow inverse method on it Functionally nothing changed. --- .../models/account_journal_dashboard.py | 5 - .../views/account_journal_dashboard_view.xml | 2 +- addons/project/models/project.py | 27 ++-- .../static/src/less/project_dashboard.less | 14 -- addons/project/views/project_views.xml | 3 +- addons/sales_team/models/crm_team.py | 17 ++- .../sales_team/views/sales_team_dashboard.xml | 6 +- .../web/static/src/js/fields/basic_fields.js | 53 ++++++++ .../static/src/js/fields/field_registry.js | 1 + addons/web/static/src/less/fields.less | 16 +++ .../static/tests/fields/basic_fields_tests.js | 123 ++++++++++++++++++ 11 files changed, 217 insertions(+), 50 deletions(-) diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 82828965d52..361962fa29f 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -26,11 +26,6 @@ class account_journal(models.Model): kanban_dashboard_graph = fields.Text(compute='_kanban_dashboard_graph') show_on_dashboard = fields.Boolean(string='Show journal on dashboard', help="Whether this journal should be displayed on the dashboard or not", default=True) - @api.multi - def toggle_favorite(self): - self.write({'show_on_dashboard': False if self.show_on_dashboard else True}) - return False - def _graph_title_and_key(self): if self.type == 'sale': return ['', _('Sales: Untaxed Total')] diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index 4b07b8a7010..024dd725bbf 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -209,7 +209,7 @@
Settings diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 7d46b94393d..49014a07481 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -151,6 +151,18 @@ class Project(models.Model): for project in self: project.is_favorite = self.env.user in project.favorite_user_ids + def _inverse_is_favorite(self): + favorite_projects = not_fav_projects = self.env['project.project'].sudo() + for project in self: + if self.env.user in project.favorite_user_ids: + favorite_projects |= project + else: + not_fav_projects |= project + + # Project User has no write access for project. + not_fav_projects.write({'favorite_user_ids': [(4, self.env.uid)]}) + favorite_projects.write({'favorite_user_ids': [(3, self.env.uid)]}) + def _get_default_favorite_user_ids(self): return [(6, 0, [self.env.uid])] @@ -172,7 +184,7 @@ class Project(models.Model): 'res.users', 'project_favorite_user_rel', 'project_id', 'user_id', default=_get_default_favorite_user_ids, string='Members') - is_favorite = fields.Boolean(compute='_compute_is_favorite', string='Show Project on dashboard', + is_favorite = fields.Boolean(compute='_compute_is_favorite', inverse='_inverse_is_favorite', string='Show Project on dashboard', help="Whether this project should be displayed on the dashboard or not") label_tasks = fields.Char(string='Use Tasks as', default='Tasks', help="Gives label to tasks on project's kanban view.") tasks = fields.One2many('project.task', 'project_id', string="Task Activities") @@ -278,19 +290,6 @@ class Project(models.Model): self.mapped('tasks').message_unsubscribe(partner_ids=partner_ids, channel_ids=channel_ids) return super(Project, self).message_unsubscribe(partner_ids=partner_ids, channel_ids=channel_ids) - @api.multi - def toggle_favorite(self): - favorite_projects = not_fav_projects = self.env['project.project'].sudo() - for project in self: - if self.env.user in project.favorite_user_ids: - favorite_projects |= project - else: - not_fav_projects |= project - - # Project User has no write access for project. - not_fav_projects.write({'favorite_user_ids': [(4, self.env.uid)]}) - favorite_projects.write({'favorite_user_ids': [(3, self.env.uid)]}) - @api.multi def close_dialog(self): return {'type': 'ir.actions.act_window_close'} diff --git a/addons/project/static/src/less/project_dashboard.less b/addons/project/static/src/less/project_dashboard.less index b1e966c8f87..54851046a27 100644 --- a/addons/project/static/src/less/project_dashboard.less +++ b/addons/project/static/src/less/project_dashboard.less @@ -38,20 +38,6 @@ } } - .o_dashboard_star { - font-size: 16px; - - &.fa-star-o { - color: @odoo-main-color-muted; - &:hover { - color: gold; - } - } - &.fa-star { - color: gold; - } - } - .o_project_kanban_boxes { width: 30%; .o-flex(0, 0, auto); diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml index 23397729a79..84bfe4b4338 100644 --- a/addons/project/views/project_views.xml +++ b/addons/project/views/project_views.xml @@ -263,7 +263,6 @@ -
@@ -296,7 +295,7 @@
- +
diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py index 99333bc52c5..5aeaba6aeec 100644 --- a/addons/sales_team/models/crm_team.py +++ b/addons/sales_team/models/crm_team.py @@ -55,7 +55,7 @@ class CrmTeam(models.Model): default=_get_default_favorite_user_ids) is_favorite = fields.Boolean( string='Show on dashboard', - compute='_compute_is_favorite', inverse='_set_is_favorite', + compute='_compute_is_favorite', inverse='_inverse_is_favorite', help="Favorite teams to display them in the dashboard and access them easily.") reply_to = fields.Char(string='Reply-To', help="The email address put in the 'Reply-To' of all emails sent by Odoo about cases in this sales channel") @@ -95,6 +95,13 @@ class CrmTeam(models.Model): for team in self: team.is_favorite = self.env.user in team.favorite_user_ids + def _inverse_is_favorite(self): + sudoed_self = self.sudo() + to_fav = sudoed_self.filtered(lambda team: self.env.user not in team.favorite_user_ids) + to_fav.write({'favorite_user_ids': [(4, self.env.uid)]}) + (sudoed_self - to_fav).write({'favorite_user_ids': [(3, self.env.uid)]}) + return True + def _graph_get_dates(self, today): """ return a coherent start and end date for the dashboard graph according to the graph settings. """ @@ -286,14 +293,6 @@ class CrmTeam(models.Model): self._add_members_to_favorites() return res - @api.multi - def toggle_favorite(self): - sudoed_self = self.sudo() - to_fav = sudoed_self.filtered(lambda team: self.env.user not in team.favorite_user_ids) - to_fav.write({'favorite_user_ids': [(4, self.env.uid)]}) - (sudoed_self - to_fav).write({'favorite_user_ids': [(3, self.env.uid)]}) - return True - def _add_members_to_favorites(self): for team in self: team.favorite_user_ids = [(4, member.id) for member in team.member_ids] diff --git a/addons/sales_team/views/sales_team_dashboard.xml b/addons/sales_team/views/sales_team_dashboard.xml index 246029b70a1..a89c050dcaa 100644 --- a/addons/sales_team/views/sales_team_dashboard.xml +++ b/addons/sales_team/views/sales_team_dashboard.xml @@ -70,11 +70,7 @@
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 2de5a73802d..2d18a0ddb56 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1449,6 +1449,58 @@ var StateSelectionWidget = AbstractField.extend({ }, }); +var FavoriteWidget = AbstractField.extend({ + className: 'o_favorite', + events: { + 'click': '_setFavorite' + }, + supportedFieldTypes: ['boolean'], + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * A boolean field is always set since false is a valid value. + * + * @override + */ + isSet: function () { + return true; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Render favorite icon based on state + * + * @override + * @private + */ + _render: function () { + var template = this.attrs.nolabel ? '' : ' %s'; + this.$el.empty().append(_.str.sprintf(template, this.value ? 'fa-star' : 'fa-star-o', this.value ? _t('Remove from Favorites') : _t('Add to Favorites'))); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Toggle favorite state + * + * @private + * @param {MouseEvent} event + */ + _setFavorite: function (event) { + event.preventDefault(); + event.stopPropagation(); + this._setValue(!this.value); + }, +}); + var LabelSelection = AbstractField.extend({ supportedFieldTypes: ['selection'], @@ -2350,6 +2402,7 @@ return { AttachmentImage: AttachmentImage, LabelSelection: LabelSelection, StateSelectionWidget: StateSelectionWidget, + FavoriteWidget: FavoriteWidget, PriorityWidget: PriorityWidget, StatInfo: StatInfo, UrlWidget: UrlWidget, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 91bba587117..019a98e51f1 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -40,6 +40,7 @@ registry .add('label_selection', basic_fields.LabelSelection) .add('state_selection', basic_fields.StateSelectionWidget) .add('kanban_state_selection', basic_fields.StateSelectionWidget) + .add('boolean_favorite', basic_fields.FavoriteWidget) .add('boolean_button', basic_fields.FieldBooleanButton) .add('boolean_toggle', basic_fields.BooleanToggle) .add('statinfo', basic_fields.StatInfo) diff --git a/addons/web/static/src/less/fields.less b/addons/web/static/src/less/fields.less index 02885ef3514..8463dd312cc 100644 --- a/addons/web/static/src/less/fields.less +++ b/addons/web/static/src/less/fields.less @@ -180,6 +180,22 @@ } } + // Favorite + &.o_favorite { + i.fa { + font-size: 16px; + } + i.fa-star-o { + color: @odoo-main-color-muted; + &:hover { + color: gold; + } + } + i.fa-star { + color: gold; + } + } + // Specific success color for toggle_button widget .fa.o_toggle_button_success { color: @brand-success; diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 508e86d9cd3..c2ab8121c16 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -2997,6 +2997,129 @@ QUnit.module('basic_fields', { }); + QUnit.module('FavoriteWidget'); + + QUnit.test('favorite widget in kanban view', function (assert) { + assert.expect(4); + + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '' + + '
' + + '' + + '
' + + '
' + + '
' + + '
', + domain: [['id', '=', 1]], + }); + + assert.strictEqual(kanban.$('.o_kanban_record .o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + assert.strictEqual(kanban.$('.o_kanban_record .o_field_widget.o_favorite > a').text(), ' Remove from Favorites', + 'the label should say "Remove from Favorites"'); + + // click on favorite + kanban.$('.o_field_widget.o_favorite').click(); + assert.strictEqual(kanban.$('.o_kanban_record .o_field_widget.o_favorite > a i.fa.fa-star').length, 0, + 'should not be favorite'); + assert.strictEqual(kanban.$('.o_kanban_record .o_field_widget.o_favorite > a').text(), ' Add to Favorites', + 'the label should say "Add to Favorites"'); + + kanban.destroy(); + }); + + QUnit.test('favorite widget in form view', function (assert) { + assert.expect(10); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + }); + + assert.strictEqual(form.$('.o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a').text(), ' Remove from Favorites', + 'the label should say "Remove from Favorites"'); + + // click on favorite + form.$('.o_field_widget.o_favorite').click(); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a i.fa.fa-star').length, 0, + 'should not be favorite'); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a').text(), ' Add to Favorites', + 'the label should say "Add to Favorites"'); + + // switch to edit mode + form.$buttons.find('.o_form_button_edit').click(); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a i.fa.fa-star-o').length, 1, + 'should not be favorite'); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a').text(), ' Add to Favorites', + 'the label should say "Add to Favorites"'); + + // click on favorite + form.$('.o_field_widget.o_favorite').click(); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a').text(), ' Remove from Favorites', + 'the label should say "Remove from Favorites"'); + + // save + form.$buttons.find('.o_form_button_save').click(); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + assert.strictEqual(form.$('.o_field_widget.o_favorite > a').text(), ' Remove from Favorites', + 'the label should say "Remove from Favorites"'); + + form.destroy(); + }); + + QUnit.test('favorite widget in editable list view without label', function (assert) { + assert.expect(4); + + var list = createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '', + }); + + assert.strictEqual(list.$('.o_data_row:first .o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + + // switch to edit mode + list.$('tbody td:not(.o_list_record_selector)').first().click(); + assert.strictEqual(list.$('.o_data_row:first .o_field_widget.o_favorite > a i.fa.fa-star').length, 1, + 'should be favorite'); + + // click on favorite + list.$('.o_data_row:first .o_field_widget.o_favorite').click(); + assert.strictEqual(list.$('.o_data_row:first .o_field_widget.o_favorite > a i.fa.fa-star').length, 0, + 'should not be favorite'); + + // save + list.$buttons.find('.o_list_button_save').click(); + assert.strictEqual(list.$('.o_data_row:first .o_field_widget.o_favorite > a i.fa.fa-star-o').length, 1, + 'should not be favorite'); + + list.destroy(); + }); + + QUnit.module('LabelSelectionWidget'); QUnit.test('label_selection widget in form view', function (assert) {