[MERGE][IMP] mail, note: improve activity summary view and use

Main specifications
 * set mail as dependency on contacts as activity view is used in contact
   that does not depend on mail -> crash;
 * update icon on the reminders: use calendar instead of clock;
 * add an icon to Contact in the systray;
 * add the Activity icon in the upper right corner of the Contact;
 * sort Activities by date: from more urgent to less urgent;
 * on the contact name in the activity view, the name is clickable but the
   cursor is wrong: use the little hand as a cursor;

Please see sub-commits for more details. Thanks to @alexkuhn for
reviewing and @est-odoo for testing.

This merge is linked to task ID 1882545 and closes PR #26797.
This commit is contained in:
Thibault Delavallée
2018-09-07 15:30:56 +02:00
12 changed files with 100 additions and 8 deletions
+2
View File
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+1 -1
View File
@@ -10,7 +10,7 @@
This module gives you a quick view of your contacts directory, accessible from your home page.
You can track your vendors, customers and other contacts.
""",
'depends': ['base'],
'depends': ['base', 'mail'],
'data': [
'views/contact_views.xml',
],
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import res_users
+25
View File
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, modules
class Users(models.Model):
_name = 'res.users'
_inherit = ['res.users']
@api.model
def systray_get_activities(self):
""" Update the systray icon of res.partner activities to use the
contact application one instead of base icon. """
activities = super(Users, self).systray_get_activities()
for activity in activities:
if activity['model'] != 'res.partner':
continue
activity['icon'] = modules.module.get_module_icon('contacts')
activity['actions'] = [{
'icon': 'fa-clock-o',
'name': 'Summary',
'action_xmlid': 'contacts.action_contacts_summary'
}]
return activities
+16
View File
@@ -34,6 +34,22 @@
<field name="act_window_id" ref="action_contacts"/>
</record>
<record id="action_contacts_summary" model="ir.actions.act_window">
<field name="name">Contacts</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">activity,kanban,tree,form</field>
<field name="search_view_id" ref="base.view_res_partner_filter"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a contact in your address book
</p><p>
Odoo helps you to easily track all activities related to a customer.
</p>
</field>
</record>
<menuitem name="Contacts"
id="menu_contacts"
sequence="4"
+11 -4
View File
@@ -408,16 +408,22 @@ class MailActivity(models.Model):
def get_activity_data(self, res_model, domain):
res = self.env[res_model].search(domain)
activity_domain = [('res_id', 'in', res.ids), ('res_model', '=', res_model)]
grouped_activities = self.env['mail.activity'].read_group(activity_domain, ['res_id', 'activity_type_id', 'res_name:max(res_name)', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'], ['res_id', 'activity_type_id'], lazy=False)
grouped_activities = self.env['mail.activity'].read_group(
activity_domain,
['res_id', 'activity_type_id', 'res_name:max(res_name)', 'ids:array_agg(id)', 'date_deadline:min(date_deadline)'],
['res_id', 'activity_type_id'],
lazy=False)
activity_type_ids = self.env['mail.activity.type']
res_list = set()
res_id_to_name = {}
res_id_to_deadline = {}
activity_data = defaultdict(dict)
for group in grouped_activities:
res_id = group['res_id']
res_name = group['res_name']
activity_type_id = group['activity_type_id'][0]
activity_type_ids |= self.env['mail.activity.type'].browse(activity_type_id) # we will get the name when reading mail_template_ids
res_list.add((res_id, res_name))
res_id_to_name[res_id] = res_name
res_id_to_deadline[res_id] = group['date_deadline'] if (res_id not in res_id_to_deadline or group['date_deadline'] < res_id_to_deadline[res_id]) else res_id_to_deadline[res_id]
state = self._compute_state_from_date(group['date_deadline'], self.user_id.sudo().tz)
activity_data[res_id][activity_type_id] = {
'count': group['__count'],
@@ -426,6 +432,7 @@ class MailActivity(models.Model):
'state': state,
'o_closest_deadline': group['date_deadline'],
}
res_ids_sorted = sorted(res_id_to_deadline, key=lambda item: res_id_to_deadline[item])
activity_type_infos = []
for elem in activity_type_ids:
mail_template_info = []
@@ -435,7 +442,7 @@ class MailActivity(models.Model):
return {
'activity_types': activity_type_infos,
'res_ids': list(res_list),
'res_ids': [(rid, res_id_to_name[rid]) for rid in res_ids_sorted],
'grouped_activities': activity_data,
'model': res_model,
}
@@ -15,6 +15,7 @@ var ActivityMenu = Widget.extend({
name: 'activity_menu',
template:'mail.systray.ActivityMenu',
events: {
'click .o_mail_activity_action': '_onActivityActionClick',
'click .o_mail_preview': '_onActivityFilterClick',
'show.bs.dropdown': '_onActivityMenuShow',
},
@@ -98,6 +99,17 @@ var ActivityMenu = Widget.extend({
// Handlers
//------------------------------------------------------------
/**
* Redirect to specific action given its xml id
* @private
* @param {MouseEvent} ev
*/
_onActivityActionClick: function (ev) {
ev.stopPropagation();
var actionXmlid = $(ev.currentTarget).data('action_xmlid');
this.do_action(actionXmlid);
},
/**
* Redirect to particular model view
* @private
@@ -33,6 +33,7 @@
color: #008784;
&:hover {
color: #666;
cursor: pointer;
}
cursor: pointer;
}
+11 -1
View File
@@ -327,9 +327,19 @@
.o_preview_title {
display: flex;
.o_preview_name {
flex: 0 1 auto;
flex: 0 0 auto;
@include o-text-overflow;
}
.o_mail_activity_action_buttons {
display: flex;
flex: 1 1 auto;
flex-flow: row-reverse wrap;
}
.o_mail_activity_action {
padding-top: 0px;
padding-bottom: 0px;
padding-right: 0px;
}
.o_preview_counter {
flex: 1 1 auto;
}
+11
View File
@@ -78,6 +78,17 @@
<span class="o_preview_name">
<t t-esc="activity.name"/>
</span>
<div t-if="activity.actions" class="o_mail_activity_action_buttons">
<t t-foreach="activity.actions" t-as="action">
<button type="button"
t-att-title="action.name"
t-att-class="'o_mail_activity_action btn btn-link fa ' + action.icon"
t-att-data-action_xmlid="action.action_xmlid"
t-att-data-res_model="activity.model"
t-att-data-model_name="activity.name">
</button>
</t>
</div>
</div>
<div t-if="activity and activity.type == 'activity'">
<button t-if="activity.overdue_count" type="button" class="btn btn-link o_activity_filter_button mr16" t-att-data-res_model="activity.model" t-att-data-model_name="activity.name" data-filter='overdue'><t t-esc="activity.overdue_count"/> Late </button>
+4
View File
@@ -37,6 +37,7 @@
display: flex;
p {
flex: 1 1 auto;
margin-bottom: 0px;
}
}
.o_note_save {
@@ -60,4 +61,7 @@
}
}
}
.o_note_set_datetime {
color: $text-muted;
}
}
+2 -2
View File
@@ -14,8 +14,8 @@
<span class="o_preview_name"><strong>Add a note</strong></span>
<div class="o_note_datetime"/>
<span class="ml4">
<a class="o_note_set_datetime text-muted">
<span class="fa fa-clock-o" role="img" aria-label="Set date and time" title="Set date and time"/>
<a class="o_note_set_datetime">
<span class="fa fa-calendar" role="img" aria-label="Set date and time" title="Set date and time"/>
</a>
</span>
</div>