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 @@
-
+
+