diff --git a/mail_activity_team/__manifest__.py b/mail_activity_team/__manifest__.py index 4963644f..d0f43d9d 100644 --- a/mail_activity_team/__manifest__.py +++ b/mail_activity_team/__manifest__.py @@ -14,10 +14,15 @@ 'mail_activity_board', ], 'data': [ + 'views/assets_backend.xml', 'security/ir.model.access.csv', 'security/mail_activity_team_security.xml', 'views/mail_activity_team_views.xml', 'views/mail_activity_views.xml', 'views/res_users_views.xml', ], + + 'qweb': [ + 'static/src/xml/systray.xml', + ], } diff --git a/mail_activity_team/models/__init__.py b/mail_activity_team/models/__init__.py index 2f37e6cc..635ee695 100644 --- a/mail_activity_team/models/__init__.py +++ b/mail_activity_team/models/__init__.py @@ -1,3 +1,4 @@ from . import mail_activity_team from . import mail_activity from . import res_users +from . import mail_activity_mixin diff --git a/mail_activity_team/models/mail_activity.py b/mail_activity_team/models/mail_activity.py index 1fccf746..90490e57 100644 --- a/mail_activity_team/models/mail_activity.py +++ b/mail_activity_team/models/mail_activity.py @@ -19,6 +19,8 @@ class MailActivity(models.Model): ('res_model_ids', 'in', model.ids)]) return self.env['mail.activity.team'].search(domain, limit=1) + user_id = fields.Many2one(required=False) + team_id = fields.Many2one( comodel_name='mail.activity.team', default=lambda s: s._get_default_team_id(), diff --git a/mail_activity_team/models/mail_activity_mixin.py b/mail_activity_team/models/mail_activity_mixin.py new file mode 100644 index 00000000..0c5035bb --- /dev/null +++ b/mail_activity_team/models/mail_activity_mixin.py @@ -0,0 +1,23 @@ +# Copyright 2018 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import api, models, fields + + +class MailActivityMixin(models.AbstractModel): + _inherit = 'mail.activity.mixin' + + activity_team_user_ids = fields.Many2many( + comodel_name='res.users', string='test field', + compute="_compute_activity_team_user_ids", + search="_search_activity_team_user_ids", + ) + + @api.depends("activity_ids") + def _compute_activity_team_user_ids(self): + for rec in self: + rec.activity_team_user_ids = rec.activity_ids.mapped( + "team_id.member_ids") + + @api.model + def _search_activity_team_user_ids(self, operator, operand): + return [('activity_ids.team_id.member_ids', operator, operand)] diff --git a/mail_activity_team/models/res_users.py b/mail_activity_team/models/res_users.py index e1f9cc33..286d3586 100644 --- a/mail_activity_team/models/res_users.py +++ b/mail_activity_team/models/res_users.py @@ -1,6 +1,6 @@ # Copyright 2018 Eficent Business and IT Consulting Services, S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields +from odoo import api, models, fields, modules class ResUsers(models.Model): @@ -11,3 +11,58 @@ class ResUsers(models.Model): relation='mail_activity_team_users_rel', string="Activity Teams", ) + + @api.model + def activity_user_count(self, user_id=False): + if not self._context.get('team_activities', False): + return super().activity_user_count() + query = """SELECT m.id, count(*), act.res_model as model, + CASE + WHEN %(today)s::date - + act.date_deadline::date = 0 Then 'today' + WHEN %(today)s::date - + act.date_deadline::date > 0 Then 'overdue' + WHEN %(today)s::date - + act.date_deadline::date < 0 Then 'planned' + END AS states, act.user_id as user_id + FROM mail_activity AS act + JOIN ir_model AS m ON act.res_model_id = m.id + WHERE team_id in ( + SELECT mail_activity_team_id + FROM mail_activity_team_users_rel + WHERE res_users_id = %(user_id)s + ) + GROUP BY m.id, states, act.res_model, act.user_id; + """ + user = user_id if user_id else self.env.uid + self.env.cr.execute(query, { + 'today': fields.Date.context_today(self), + 'user_id': user, + }) + activity_data = self.env.cr.dictfetchall() + model_ids = [a['id'] for a in activity_data] + model_names = {n[0]: n[1] for n in + self.env['ir.model'].browse(model_ids).name_get()} + + user_activities = {} + for activity in activity_data: + if not user_activities.get(activity['model']): + user_activities[activity['model']] = { + 'name': model_names[activity['id']], + 'model': activity['model'], + 'icon': modules.module.get_module_icon( + self.env[activity['model']]._original_module), + 'total_count': 0, 'today_count': 0, 'overdue_count': 0, + 'planned_count': 0, + } + user_activities[activity['model']][ + '%s_count' % activity['states']] += activity['count'] + if activity['states'] in ('today', 'overdue'): + user_activities[activity['model']]['total_count'] += activity[ + 'count'] + if activity['user_id'] == user: + user_activities[ + activity['model'] + ]['total_count'] -= activity['count'] + + return list(user_activities.values()) diff --git a/mail_activity_team/static/src/js/systray.js b/mail_activity_team/static/src/js/systray.js new file mode 100644 index 00000000..3a64a7be --- /dev/null +++ b/mail_activity_team/static/src/js/systray.js @@ -0,0 +1,99 @@ +odoo.define('mail_activity_team.systray', function (require) { + "use strict"; + + var systray = require('mail.systray'); + var session = require("web.session"); + + systray.ActivityMenu.include({ + events: _.extend({}, systray.ActivityMenu.prototype.events, { + 'click .o_filter_button': 'on_click_filter_button', + }), + start: function () { + this._super.apply(this, arguments); + this.$filter_buttons = this.$('.o_filter_button'); + this.$my_activities = this.$filter_buttons.first(); + this.filter = 'my'; + session.user_context = _.extend({}, session.user_context, { + 'team_activities': false + }); + }, + + _updateCounter: function (data) { + this._super.apply(this, arguments); + this.$('.o_new_notification_counter').text(this.activityCounter); + }, + + on_click_filter_button: function (event) { + var self = this; + + event.stopPropagation(); + self.$filter_buttons.removeClass('active'); + var $target = $(event.currentTarget); + $target.addClass('active'); + self.filter = $target.data('filter'); + + session.user_context = _.extend({}, session.user_context, { + 'team_activities': self.filter === 'team' + }); + + self._updateActivityPreview(); + + }, + _onActivityFilterClick: function (event) { + if (this.filter === 'my') { + this._super.apply(this, arguments); + } + if (this.filter === 'team') { + var data = _.extend( + {}, + $(event.currentTarget).data(), + $(event.target).data() + ); + var context = {}; + if (data.filter === 'my') { + context.search_default_activities_overdue = 1; + context.search_default_activities_today = 1; + } else { + context['search_default_activities_' + data.filter] = 1; + } + this.do_action({ + type: 'ir.actions.act_window', + name: data.model_name, + res_model: data.res_model, + views: [[false, 'kanban'], [false, 'form']], + search_view_id: [false], + domain: [ + ['activity_team_user_ids', 'in', session.uid] + ], + context:context, + }); + } + }, + _getActivityData: function(){ + var self = this; + return self._super.apply(self, arguments).then(function (data) { + session.user_context = _.extend({}, session.user_context, { + 'team_activities': !session.user_context['team_activities'], + }); + + self._rpc({ + model: 'res.users', + method: 'activity_user_count', + kwargs: { + context: session.user_context, + }, + }).then(function (data) { + self.activityCounter += _.reduce(data, function( + total_count, p_data + ){ return total_count + p_data.total_count; }, 0); + self.$('.o_new_notification_counter').text(self.activityCounter); + self.$el.toggleClass('o_no_notification', !self.activityCounter); + session.user_context = _.extend({}, session.user_context, { + 'team_activities': !session.user_context['team_activities'], + }); + }); + }); + } + }); + +}); diff --git a/mail_activity_team/static/src/less/systray.less b/mail_activity_team/static/src/less/systray.less new file mode 100644 index 00000000..9690aef1 --- /dev/null +++ b/mail_activity_team/static/src/less/systray.less @@ -0,0 +1,7 @@ +.o_new_notification_counter { + .o-position-absolute(@top: 20%, @right: 1px); + background: @odoo-brand-optional; + color: white; + padding: 0em 0.3em; + font-size: 0.7em; +} diff --git a/mail_activity_team/static/src/xml/systray.xml b/mail_activity_team/static/src/xml/systray.xml new file mode 100644 index 00000000..5d76f1c6 --- /dev/null +++ b/mail_activity_team/static/src/xml/systray.xml @@ -0,0 +1,18 @@ + + + + + +
  • +
    + + +
    +
  • +
    + + + +
    + +
    diff --git a/mail_activity_team/tests/test_mail_activity_team.py b/mail_activity_team/tests/test_mail_activity_team.py index 413e4122..908a657d 100644 --- a/mail_activity_team/tests/test_mail_activity_team.py +++ b/mail_activity_team/tests/test_mail_activity_team.py @@ -116,3 +116,11 @@ class TestMailActivityTeam(TransactionCase): self.team2.member_ids = [(3, self.employee.id)] self.team2._onchange_member_ids() self.assertFalse(self.team2.user_id) + + def test_activity_count(self): + res = self.env['res.users'].with_context( + {'team_activities': True} + ).activity_user_count( + user_id=self.employee.id + ) + self.assertEqual(res[0]['total_count'], 0) diff --git a/mail_activity_team/views/assets_backend.xml b/mail_activity_team/views/assets_backend.xml new file mode 100644 index 00000000..51f10ca8 --- /dev/null +++ b/mail_activity_team/views/assets_backend.xml @@ -0,0 +1,10 @@ + + +