From 2dda6e20df056c09239aa8ee7374c62d144c72c1 Mon Sep 17 00:00:00 2001 From: Dajuayen Date: Fri, 2 Nov 2018 10:38:21 +0100 Subject: [PATCH] [ADD][mail_activity_board] Add new module that insert activities board in boards. (#283) * [ADD] Module that insert activities board. * [FIX] Author error in __manifest__ file and style changes. * [FIX] Fix replace in view, rename files and style changes. * [FIX] Enumerated list ends without a blank line; unexpected unindent. * [FIX] Name fail. * [FIX] Bug in view. * [FIX] Add button Activities in mail.thread and readme folder. Others improvements in style of code. * [FIX] Type 'tree' not found in registry: problem solved. * [FIX] Dependence change: 'mail' for 'calendar'. * [FIX] Eliminated unnecessary imports. * [FIX] Bugs about js and if/else. * [FIX] Improvements following guide lines and eliminating unnecessary attributes in views. * [ADD] Added counter in the 'Activities List' button. * [FIX] Bugs in javascript with 'Activities' button. * [ADD] Tests folder. * [FIX] Deleted references to modules not installed. * [FIX] Formatting javascript. * [FIX] Bug: added a soft line before a class. * [FIX] Bug: https://github.com/OCA/social/pull/283#discussion_r204302325 * [FIX] Escaping 'lt' in xml file. Bug: https://github.com/OCA/social/pull/283#discussion_r204302325 * [FIX] The meeting attendees are shown in kanban mode on the meeting board. * [FIX] Hide in form view of the activity board the assistant field if the activity is not a meeting type or if there are no assistants. * [FIX] Change to default kanban view for partners. --- mail_activity_board/README.rst | 21 ++ mail_activity_board/__init__.py | 1 + mail_activity_board/__manifest__.py | 24 ++ mail_activity_board/models/__init__.py | 2 + mail_activity_board/models/mail_activity.py | 45 ++++ .../models/mail_activity_mixin.py | 32 +++ mail_activity_board/readme/CONTRIBUTORS.rst | 3 + mail_activity_board/readme/DESCRIPTION.rst | 2 + mail_activity_board/readme/USAGE.rst | 9 + .../static/src/js/override_chatter.js | 33 +++ .../static/src/xml/inherit_chatter.xml | 13 ++ mail_activity_board/tests/__init__.py | 1 + .../tests/test_mail_activity_board.py | 136 ++++++++++++ .../views/mail_activity_view.xml | 210 ++++++++++++++++++ mail_activity_board/views/templates.xml | 8 + 15 files changed, 540 insertions(+) create mode 100644 mail_activity_board/README.rst create mode 100644 mail_activity_board/__init__.py create mode 100644 mail_activity_board/__manifest__.py create mode 100644 mail_activity_board/models/__init__.py create mode 100644 mail_activity_board/models/mail_activity.py create mode 100644 mail_activity_board/models/mail_activity_mixin.py create mode 100644 mail_activity_board/readme/CONTRIBUTORS.rst create mode 100644 mail_activity_board/readme/DESCRIPTION.rst create mode 100644 mail_activity_board/readme/USAGE.rst create mode 100644 mail_activity_board/static/src/js/override_chatter.js create mode 100644 mail_activity_board/static/src/xml/inherit_chatter.xml create mode 100644 mail_activity_board/tests/__init__.py create mode 100644 mail_activity_board/tests/test_mail_activity_board.py create mode 100644 mail_activity_board/views/mail_activity_view.xml create mode 100644 mail_activity_board/views/templates.xml diff --git a/mail_activity_board/README.rst b/mail_activity_board/README.rst new file mode 100644 index 0000000..21cd785 --- /dev/null +++ b/mail_activity_board/README.rst @@ -0,0 +1,21 @@ +**This file is going to be generated by oca-gen-addon-readme.** + +*Manual changes will be overwritten.* + +Please provide content in the ``readme`` directory: + +* **DESCRIPTION.rst** (required) +* INSTALL.rst (optional) +* CONFIGURE.rst (optional) +* **USAGE.rst** (optional, highly recommended) +* DEVELOP.rst (optional) +* ROADMAP.rst (optional) +* HISTORY.rst (optional, recommended) +* **CONTRIBUTORS.rst** (optional, highly recommended) +* CREDITS.rst (optional) + +Content of this README will also be drawn from the addon manifest, +from keys such as name, authors, maintainers, development_status, +and license. + +A good, one sentence summary in the manifest is also highly recommended. diff --git a/mail_activity_board/__init__.py b/mail_activity_board/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/mail_activity_board/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mail_activity_board/__manifest__.py b/mail_activity_board/__manifest__.py new file mode 100644 index 0000000..8d0f015 --- /dev/null +++ b/mail_activity_board/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2018 David Juaneda - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'Activities board', + 'summary': 'Add Activity Boards', + 'version': '11.0.1.0.0', + 'development_status': 'Beta', + 'category': 'Social Network', + 'website': 'https://github.com/OCA/social', + 'author': 'SDi, David Juaneda, Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'installable': True, + 'depends': [ + 'calendar', + 'board', + ], + 'data': [ + 'views/templates.xml', + 'views/mail_activity_view.xml', + ], + 'qweb': [ + 'static/src/xml/inherit_chatter.xml', + ] +} diff --git a/mail_activity_board/models/__init__.py b/mail_activity_board/models/__init__.py new file mode 100644 index 0000000..caf9c18 --- /dev/null +++ b/mail_activity_board/models/__init__.py @@ -0,0 +1,2 @@ +from . import mail_activity +from . import mail_activity_mixin diff --git a/mail_activity_board/models/mail_activity.py b/mail_activity_board/models/mail_activity.py new file mode 100644 index 0000000..9e55c03 --- /dev/null +++ b/mail_activity_board/models/mail_activity.py @@ -0,0 +1,45 @@ +# Copyright 2018 David Juaneda - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import api, models, fields + + +class MailActivity(models.Model): + _inherit = "mail.activity" + + res_model_id_name = fields.Char( + related='res_model_id.name', string="Origin", + readonly=True) + duration = fields.Float( + related='calendar_event_id.duration', readonly=True) + calendar_event_id_start = fields.Datetime( + related='calendar_event_id.start', readonly=True) + calendar_event_id_partner_ids = fields.Many2many( + related='calendar_event_id.partner_ids', + readonly=True) + + @api.multi + def open_origin(self): + self.ensure_one() + vid = self.env[self.res_model].browse(self.res_id).get_formview_id() + response = { + 'type': 'ir.actions.act_window', + 'res_model': self.res_model, + 'view_mode': 'form', + 'res_id': self.res_id, + 'target': 'current', + 'flags': { + 'form': { + 'action_buttons': False + } + }, + 'views': [ + (vid, "form") + ] + } + return response + + @api.model + def action_activities_board(self): + action = self.env.ref( + 'mail_activity_board.open_boards_activities').read()[0] + return action diff --git a/mail_activity_board/models/mail_activity_mixin.py b/mail_activity_board/models/mail_activity_mixin.py new file mode 100644 index 0000000..4cb41cd --- /dev/null +++ b/mail_activity_board/models/mail_activity_mixin.py @@ -0,0 +1,32 @@ +# Copyright 2018 David Juaneda - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models + + +class MailActivityMixin(models.AbstractModel): + _inherit = 'mail.activity.mixin' + + def redirect_to_activities(self, **kwargs): + """Redirects to the list of activities of the object shown. + + Redirects to the activity board and configures the domain so that + only those activities that are related to the object shown are + displayed. + + Add to the title of the view the name the class of the object from + which the activities will be displayed. + + :param kwargs: contains the id of the object and the model it's about. + + :return: action. + """ + id = kwargs.get("id") + action = self.env['mail.activity'].action_activities_board() + views = [] + for v in action['views']: + if v[1] == 'tree': + v = (v[0], 'list') + views.append(v) + action['views'] = views + action['domain'] = [('res_id', '=', id)] + return action diff --git a/mail_activity_board/readme/CONTRIBUTORS.rst b/mail_activity_board/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..e18a28e --- /dev/null +++ b/mail_activity_board/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `SDI `_: + + * David Juaneda diff --git a/mail_activity_board/readme/DESCRIPTION.rst b/mail_activity_board/readme/DESCRIPTION.rst new file mode 100644 index 0000000..99d1c55 --- /dev/null +++ b/mail_activity_board/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module adds an activity board with form, tree, kanban, calendar, pivot, graph and search views. + diff --git a/mail_activity_board/readme/USAGE.rst b/mail_activity_board/readme/USAGE.rst new file mode 100644 index 0000000..82a8548 --- /dev/null +++ b/mail_activity_board/readme/USAGE.rst @@ -0,0 +1,9 @@ +To use this module, you need to: + +#. Access to the views from menu Boards. + +A smartButton of activities is added in the mail thread from form view. +From this smartButton is linked to the activity board, to the view tree, +which shows the activities related to the opportunity. + +From the form view of the activity you can navigate to the origin of the activity. diff --git a/mail_activity_board/static/src/js/override_chatter.js b/mail_activity_board/static/src/js/override_chatter.js new file mode 100644 index 0000000..d3be65a --- /dev/null +++ b/mail_activity_board/static/src/js/override_chatter.js @@ -0,0 +1,33 @@ +/* Copyright 2018 David Juaneda + * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ +odoo.define('mail.Chatter.activity', function (require) { + "use strict"; + + var chatter = require('mail.Chatter'); + + chatter.include({ + + events: _.extend({}, chatter.prototype.events, { + 'click .o_chatter_button_list_activity': '_onListActivity', + }), + + /** + * Performs the action to redirect to the activities of the object. + * + * @private + */ + _onListActivity: function () { + this._rpc({ + model: this.record.model, + method: 'redirect_to_activities', + args: [[]], + kwargs: { + 'id':this.record.res_id, + 'model':this.record.model, + }, + context: this.record.getContext(), + }).then($.proxy(this, "do_action")); + }, + + }); +}); diff --git a/mail_activity_board/static/src/xml/inherit_chatter.xml b/mail_activity_board/static/src/xml/inherit_chatter.xml new file mode 100644 index 0000000..5fee3e7 --- /dev/null +++ b/mail_activity_board/static/src/xml/inherit_chatter.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/mail_activity_board/tests/__init__.py b/mail_activity_board/tests/__init__.py new file mode 100644 index 0000000..a87cb6e --- /dev/null +++ b/mail_activity_board/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mail_activity_board diff --git a/mail_activity_board/tests/test_mail_activity_board.py b/mail_activity_board/tests/test_mail_activity_board.py new file mode 100644 index 0000000..ac91c51 --- /dev/null +++ b/mail_activity_board/tests/test_mail_activity_board.py @@ -0,0 +1,136 @@ +# Copyright 2018 David Juaneda - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.tests.common import TransactionCase + + +class TestMailActivityBoardMethods(TransactionCase): + + def setUp(self): + super(TestMailActivityBoardMethods, self).setUp() + # Set up activities + + # Create a user as 'Crm Salesman' and added few groups + self.employee = self.env['res.users'].create({ + 'company_id': self.env.ref("base.main_company").id, + 'name': "Employee", + 'login': "csu", + 'email': "crmuser@yourcompany.com", + 'groups_id': [(6, 0, [self.env.ref('base.group_user').id])] + }) + + # lead_model_id = self.env['ir.model']._get('crm.lead').id + partner_model_id = self.env['ir.model']._get('res.partner').id + + ActivityType = self.env['mail.activity.type'] + self.activity1 = ActivityType.create({ + 'name': 'Initial Contact', + 'days': 5, + 'summary': 'ACT 1 : Presentation, barbecue, ... ', + 'res_model_id': partner_model_id, + }) + self.activity2 = ActivityType.create({ + 'name': 'Call for Demo', + 'days': 6, + 'summary': 'ACT 2 : I want to show you my ERP !', + 'res_model_id': partner_model_id, + }) + self.activity3 = ActivityType.create({ + 'name': 'Celebrate the sale', + 'days': 3, + 'summary': 'ACT 3 : ' + 'Beers for everyone because I am a good salesman !', + 'res_model_id': partner_model_id, + }) + + # I create an opportunity, as employee + self.partner_client = self.env.ref("base.res_partner_1") + + self.act1 = self.env['mail.activity'].sudo().create({ + 'activity_type_id': self.activity3.id, + 'note': 'Partner activity 1.', + 'res_id': self.partner_client.id, + 'res_model_id': partner_model_id, + 'user_id': self.employee.id + }) + self.act2 = self.env['mail.activity'].sudo().create({ + 'activity_type_id': self.activity2.id, + 'note': 'Partner activity 2.', + 'res_id': self.partner_client.id, + 'res_model_id': partner_model_id, + 'user_id': self.employee.id + }) + self.act3 = self.env['mail.activity'].sudo().create({ + 'activity_type_id': self.activity3.id, + 'note': 'Partner activity 3.', + 'res_id': self.partner_client.id, + 'res_model_id': partner_model_id, + 'user_id': self.employee.id + }) + + def get_view(self, activity): + action = activity.open_origin() + result = self.env[action.get('res_model')]\ + .load_views(action.get('views')) + return result.get('fields_views').get(action.get('view_mode')) + + def test_open_origin_res_partner(self): + """ This test case checks + - If the method redirects to the form view of the correct one + of an object of the 'res.partner' class to which the activity + belongs. + """ + # Id of the form view for the class 'crm.lead', type 'lead' + form_view_partner_id = self.env.ref('base.view_partner_form').id + + # Id of the form view return open_origin() + view = self.get_view(self.act1) + + # Check the next view is correct + self.assertEqual(form_view_partner_id, view.get('view_id')) + + # Id of the form view return open_origin() + view = self.get_view(self.act2) + + # Check the next view is correct + self.assertEqual(form_view_partner_id, view.get('view_id')) + + # Id of the form view return open_origin() + view = self.get_view(self.act3) + + # Check the next view is correct + self.assertEqual(form_view_partner_id, view.get('view_id')) + + def test_redirect_to_activities(self): + """ This test case checks + - if the method returns the correct action, + - if the correct activities are shown. + """ + action_id = self.env.ref( + 'mail_activity_board.open_boards_activities').id + action = self.partner_client\ + .redirect_to_activities(**{'id': self.partner_client.id}) + self.assertEqual(action.get('id'), action_id) + + kwargs = { + 'groupby': [ + "activity_type_id" + ] + } + kwargs['domain'] = action.get('domain') + + result = self.env[action.get('res_model')]\ + .load_views(action.get('views')) + fields = result.get('fields_views').get('kanban').get('fields') + kwargs['fields'] = list(fields.keys()) + + result = self.env['mail.activity'].read_group(**kwargs) + + acts = [] + for group in result: + records = self.env['mail.activity'].search_read( + domain=group.get('__domain'), fields=kwargs['fields'] + ) + acts += [id.get('id') for id in records] + + for act in acts: + self.assertIn(act, self.partner_client.activity_ids.ids) diff --git a/mail_activity_board/views/mail_activity_view.xml b/mail_activity_board/views/mail_activity_view.xml new file mode 100644 index 0000000..1db9fb3 --- /dev/null +++ b/mail_activity_board/views/mail_activity_view.xml @@ -0,0 +1,210 @@ + + + + + + + + mail.activity.boards.view.form + mail.activity + 30 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + mail.activity.boards.view.tree + mail.activity + + + + + (date_deadline < current_date) + (date_deadline == current_date) + (date_deadline > current_date) + + + + + + + + + mail.activity.boards.view.kanban + mail.activity + + + + + + + + + + + + + + + +
+
+
+ + + + +
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + mail.activity.boards.view.search + mail.activity + + + primary + + + + + + + + + + + + + + + + + + + + + + + + Activities + mail.activity + form + kanban,form + [] + {} + + + + + + + + +
diff --git a/mail_activity_board/views/templates.xml b/mail_activity_board/views/templates.xml new file mode 100644 index 0000000..b16cbb7 --- /dev/null +++ b/mail_activity_board/views/templates.xml @@ -0,0 +1,8 @@ + + + +