From b0134452c06c25aafafffc53cd9b30617cae82bd Mon Sep 17 00:00:00 2001 From: Damien Abeloos Date: Fri, 8 Jan 2021 18:11:12 +0000 Subject: [PATCH] [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 --- addons/calendar/models/__init__.py | 1 + addons/calendar/models/mail_activity_mixin.py | 18 +++++ addons/calendar/tests/__init__.py | 1 + .../tests/test_mail_activity_mixin.py | 77 +++++++++++++++++++ addons/crm/models/crm_lead.py | 8 ++ addons/crm/views/crm_lead_views.xml | 11 ++- 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 addons/calendar/models/mail_activity_mixin.py create mode 100644 addons/calendar/tests/test_mail_activity_mixin.py diff --git a/addons/calendar/models/__init__.py b/addons/calendar/models/__init__.py index 3cacb6bca87..89acd141801 100644 --- a/addons/calendar/models/__init__.py +++ b/addons/calendar/models/__init__.py @@ -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 diff --git a/addons/calendar/models/mail_activity_mixin.py b/addons/calendar/models/mail_activity_mixin.py new file mode 100644 index 00000000000..4e2b203bc76 --- /dev/null +++ b/addons/calendar/models/mail_activity_mixin.py @@ -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 diff --git a/addons/calendar/tests/__init__.py b/addons/calendar/tests/__init__.py index e16500942a3..409654f26f2 100644 --- a/addons/calendar/tests/__init__.py +++ b/addons/calendar/tests/__init__.py @@ -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 diff --git a/addons/calendar/tests/test_mail_activity_mixin.py b/addons/calendar/tests/test_mail_activity_mixin.py new file mode 100644 index 00000000000..826db01eb51 --- /dev/null +++ b/addons/calendar/tests/test_mail_activity_mixin.py @@ -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") diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 64c443c3e0f..e5e85819024 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -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() diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index 83838fa8c5d..392c1f974f3 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -678,10 +678,11 @@ - + + @@ -696,9 +697,13 @@ -