[IMP] crm: add a reschedule action for activities

* Replace the "Snooze 7d" action for lead-related activities linked to
  a calendar event by a "Reschedule" action, which will redirect the user
  to the calendar view with the focus on the next event related to the
  opportunity
    - The "Snooze 7d" action only impacted the activity and not the meeting,
      and snoozing a meeting is not very logical anyway. The "Reschedule" fits
      better in this use case.

* It was discussed to show only a modal allowing to reschedule the next
  calendar event, but that could lead to inconsistent states (out of order
  sequence of scheduled events, if the first is rescheduled after the
  second).

* The reschedule option will be shown next to any activity related to a
  calendar_event regardless of its activity type. There are some edge cases
  that we may want to change in the future :
    - When changing the activity type of a 'meeting' activity associated to
      a calendar event to any other activity type, but without removing the
      calendar event, the reschedule action is still showing (since the event
      is still ongoing and was not deleted from the calendar).
    - Conversely, with a meeting which is not (yet?) related to a calendar event,
      the reschedule action will not be available.

* Add a new field to MailActivityMixin in the calendar module context. This
  field is the event linked to the next activity, if there is one such event.
  It is used to check whether the reschedule action should be available to the user.

* Add a test for this new field to validate the expected behavior.

* Add the priority widget to the priority field in the opportunity tree view

Task ID : 2410217
PR : https://github.com/odoo/odoo/pull/63370
This commit is contained in:
Damien Abeloos
2021-03-03 12:43:22 +00:00
committed by Damien (abd)
parent 67c7460be1
commit b0134452c0
6 changed files with 113 additions and 3 deletions
+1
View File
@@ -11,4 +11,5 @@ from . import calendar_contact
from . import calendar_event_type
from . import calendar_recurrence
from . import mail_activity
from . import mail_activity_mixin
from . import res_users
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class MailActivityMixin(models.AbstractModel):
_inherit = 'mail.activity.mixin'
activity_calendar_event_id = fields.Many2one(
'calendar.event', string="Next Activity Calendar Event",
compute='_compute_activity_calendar_event_id', groups="base.group_user")
@api.depends('activity_ids.calendar_event_id')
def _compute_activity_calendar_event_id(self):
"""This computes the calendar event of the next activity.
It evaluates to false if there is no such event."""
for record in self:
record.activity_calendar_event_id = record.activity_ids[:1].calendar_event_id
+1
View File
@@ -8,3 +8,4 @@ from . import test_calendar_controller
from . import test_calendar_recurrent_event_case2
from . import test_event_recurrence
from . import test_event_notifications
from . import test_mail_activity_mixin
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, time
from dateutil.relativedelta import relativedelta
import pytz
from odoo import Command
from odoo import tests
from odoo.addons.mail.tests.common import MailCommon
@tests.tagged('mail_activity_mixin')
class TestMailActivityMixin(MailCommon):
@classmethod
def setUpClass(cls):
super(TestMailActivityMixin, cls).setUpClass()
# using res.partner as the model inheriting from mail.activity.mixin
cls.test_record = cls.env['res.partner'].with_context(cls._test_context).create({'name': 'Test'})
cls.activity_type_1 = cls.env['mail.activity.type'].create({
'name': 'Calendar Activity Test Default',
'summary': 'default activity',
'res_model_id': cls.env['ir.model']._get('base.res_partner').id,
})
cls.env['ir.model.data'].create({
'name': cls.activity_type_1.name.lower().replace(' ', '_'),
'module': 'calendar',
'model': cls.activity_type_1._name,
'res_id': cls.activity_type_1.id,
})
# reset ctx
cls._reset_mail_context(cls.test_record)
def test_activity_calendar_event_id(self):
"""Test the computed field "activity_calendar_event_id" which is the event of the
next activity. It must evaluate to False if the next activity is not related to an event"""
def create_event(name, event_date):
return self.env['calendar.event'].create({
'name': name,
'start': datetime.combine(event_date, time(12, 0, 0)),
'stop': datetime.combine(event_date, time(14, 0, 0)),
})
def schedule_meeting_activity(record, date_deadline, calendar_event=False):
meeting = record.activity_schedule('calendar.calendar_activity_test_default', date_deadline=date_deadline)
meeting.calendar_event_id = calendar_event
return meeting
group_partner_manager = self.env['ir.model.data'].xmlid_to_res_id('base.group_partner_manager')
self.user_employee.write({
'tz': self.user_admin.tz,
'groups_id': [Command.link(group_partner_manager)]
})
with self.with_user('employee'):
test_record = self.env['res.partner'].browse(self.test_record.id)
self.assertEqual(test_record.activity_ids, self.env['mail.activity'])
now_utc = datetime.now(pytz.UTC)
now_user = now_utc.astimezone(pytz.timezone(self.env.user.tz or 'UTC'))
today_user = now_user.date()
date1 = today_user + relativedelta(days=1)
date2 = today_user + relativedelta(days=2)
ev1 = create_event('ev1', date1)
ev2 = create_event('ev2', date2)
act1 = schedule_meeting_activity(test_record, date1)
schedule_meeting_activity(test_record, date2, ev2)
self.assertFalse(test_record.activity_calendar_event_id, "The next activity does not have a calendar event")
act1.calendar_event_id = ev1
self.assertEqual(test_record.activity_calendar_event_id.name, ev1.name, "This should be the calendar event of the next activity")
+8
View File
@@ -946,6 +946,14 @@ class Lead(models.Model):
}
return action
def action_reschedule_meeting(self):
self.ensure_one()
action = self.action_schedule_meeting()
next_activity = self.activity_ids.filtered(lambda activity: activity.user_id == self.env.user)[:1]
if next_activity.calendar_event_id:
action['context']['initial_date'] = next_activity.calendar_event_id.start
return action
def action_snooze(self):
self.ensure_one()
today = date.today()
+8 -3
View File
@@ -678,10 +678,11 @@
<field name="user_id" widget="many2one_avatar_user" optional="show"
domain="[('share', '=', False)]"/>
<field name="team_id" optional="hide"/>
<field name="priority" optional="hide"/>
<field name="priority" optional="hide" widget="priority"/>
<field name="activity_ids" widget="list_activity"/>
<field name="activity_user_id" optional="hide" string="Activity by" widget="many2one_avatar_user"/>
<field name="activity_date_deadline_my" string="My Deadline" widget="remaining_days" options="{'allow_order': '1'}"/>
<field name="activity_calendar_event_id" invisible="1"/>
<field name="campaign_id" optional="hide"/>
<field name="medium_id" optional="hide"/>
<field name="source_id" optional="hide"/>
@@ -696,9 +697,13 @@
<field name="tag_ids" optional="hide" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="referred" invisible="1"/>
<field name="message_needaction" invisible="1"/>
<button name="action_snooze" class="text-warning" type="object" string="Snooze 7d" icon="fa-bell-slash"
attrs="{'invisible': [('activity_date_deadline_my', '=', False)]}"/>
<button name="%(crm.action_lead_mail_compose)d" type="action" string="Email" icon="fa-envelope"/>
<button name="action_reschedule_meeting" class="text-warning" type="object" string="Reschedule"
icon="fa-calendar" attrs="{'invisible': ['|', ('activity_date_deadline_my', '=', False),
('activity_calendar_event_id', '=', False)]}"/>
<button name="action_snooze" class="text-warning" type="object" string="Snooze 7d"
icon="fa-bell-slash" attrs="{'invisible': ['|', ('activity_date_deadline_my', '=', False),
('activity_calendar_event_id', '!=', False)]}"/>
</tree>
</field>
</record>