summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2017-11-17 12:59:00 +0100
committerThibault Delavallée <tde@odoo.com>2018-03-27 15:00:46 +0200
commit60b81e00b27edd95d31d668687561e0b2dd7c714 (patch)
treec9153842fd664bb343587d8e3fb29d2ac2549f8a /addons
parent093dd9b5122bc8a627d758a86839b42d3fdc0bc4 (diff)
[IMP] mail: add tool methods to manage activities directly from the mixin
This commit adds tools methods related to activities in the mail.activity mixin. It gives to models inheriting from the activity mixin an easy-to-use API to schedule, unlink or mark activities as done. Purpose of those methods is to avoid having people manually managing activities in the code to hide the technical details of the activities model, notably access rights or activity types. A field is added on activity model to indicate they have been generated automatically. This way when rescheduling or unlinking based on some specific activity types we do not change user-created activities. Those methods include scheduling activities, changing their dates, marking them as done or unlinking them. Future commits will use those methods in various addons to automatically generate activities based on workflow we want to implement. This commit also adds tests for the newly added code. Future commits should probably have a look at activity security and add some tests cases to check it is correctly taken into account. It is considered a bit out of scope for this task. Thanks to @jem-odoo for its in-depth review of this commit. Well thanks for other commits also.
Diffstat (limited to 'addons')
-rw-r--r--addons/mail/models/mail_activity.py113
-rw-r--r--addons/test_mail/__manifest__.py1
-rw-r--r--addons/test_mail/data/data.xml23
-rw-r--r--addons/test_mail/models/test_mail_models.py9
-rw-r--r--addons/test_mail/tests/test_mail_activity.py72
-rw-r--r--addons/test_mail/tests/test_performance.py20
6 files changed, 234 insertions, 4 deletions
diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py
index 1259e7395f8..0f849526b90 100644
--- a/addons/mail/models/mail_activity.py
+++ b/addons/mail/models/mail_activity.py
@@ -93,6 +93,9 @@ class MailActivity(models.Model):
active = fields.Boolean(
'Open', default=True,
help='Done reminders should be archived instead of marked as done.')
+ automated = fields.Boolean(
+ 'Automated activity', readonly=True,
+ help='Indicates this activity has been created automatically and not by any user.')
# description
user_id = fields.Many2one(
'res.users', 'Assigned to',
@@ -345,7 +348,15 @@ class MailActivityMixin(models.AbstractModel):
There is also a kanban widget defined. It defines a small widget to integrate
in kanban vignettes. It allow to manage activities directly from the kanban
view. Use widget="kanban_activity" on activitiy_ids field in kanban view to
- use it."""
+ use it.
+
+ Some context keys allow to control the mixin behavior. Use those in some
+ specific cases like import
+
+ * ``mail_activity_automation_skip``: skip activities automation; it means
+ no automated activities will be generated, updated or unlinked, allowing
+ to save computation and avoid generating unwanted activities;
+ """
_name = 'mail.activity.mixin'
_description = 'Activity Mixin'
@@ -432,3 +443,103 @@ class MailActivityMixin(models.AbstractModel):
[('res_model', '=', self._name), ('res_id', 'in', record_ids)]
).unlink()
return result
+
+ def activity_schedule(self, act_type_xmlid='', date_deadline=None, summary='', note='', **act_values):
+ """ Schedule an activity on each record of the current record set.
+ This method allow to provide as parameter act_type_xmlid. This is an
+ xml_id of activity type instead of directly giving an activity_type_id.
+ It is useful to avoid having various "env.ref" in the code and allow
+ to let the mixin handle access rights.
+ """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ if not date_deadline:
+ date_deadline = fields.Date.today()
+ if act_type_xmlid:
+ activity_type = self.sudo().env.ref(act_type_xmlid)
+ else:
+ activity_type = self.env['mail.activity.type'].sudo().browse(act_values['activity_type_id'])
+
+ model_id = self.env['ir.model']._get(self._name).id
+ activities = self.env['mail.activity']
+ for record in self:
+ create_vals = {
+ 'activity_type_id': activity_type.id,
+ 'summary': summary or activity_type.summary,
+ 'automated': True,
+ 'note': note,
+ 'date_deadline': date_deadline,
+ 'res_model_id': model_id,
+ 'res_id': record.id,
+ }
+ create_vals.update(act_values)
+ activities |= self.env['mail.activity'].create(create_vals)
+ return activities
+
+ def activity_reschedule(self, act_type_xmlids, user_id=None, date_deadline=None):
+ """ Reschedule some automated activities. Activities to reschedule are
+ selected based on type xml ids and optionally by user. Purpose is to be
+ able to change the deadline, not anything else currently. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ activities = self.env['mail.activity'].search(domain)
+ if activities:
+ activities.write({
+ 'date_deadline': date_deadline,
+ })
+ return activities
+
+ def activity_feedback(self, act_type_xmlids, user_id=None, feedback=None):
+ """ Set activities as done, limiting to some activity types and
+ optionally to a given user. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ activities = self.env['mail.activity'].search(domain)
+ if activities:
+ activities.action_feedback(feedback=feedback)
+ return True
+
+ def activity_unlink(self, act_type_xmlids, user_id=None):
+ """ Unlink activities, limiting to some activity types and optionally
+ to a given user. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ self.env['mail.activity'].search(domain).unlink()
+ return True
diff --git a/addons/test_mail/__manifest__.py b/addons/test_mail/__manifest__.py
index 51d9cc7fe42..40d43883d4b 100644
--- a/addons/test_mail/__manifest__.py
+++ b/addons/test_mail/__manifest__.py
@@ -12,6 +12,7 @@ tests independently to functional aspects of other models. """,
'depends': ['test_performance', 'mail'],
'data': [
'security/ir.model.access.csv',
+ 'data/data.xml',
],
'demo': [
'data/demo.xml',
diff --git a/addons/test_mail/data/data.xml b/addons/test_mail/data/data.xml
new file mode 100644
index 00000000000..19be18eb2d6
--- /dev/null
+++ b/addons/test_mail/data/data.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+ <record id="mail_act_test_todo" model="mail.activity.type">
+ <field name="name">Do Stuff</field>
+ <field name="summary">Really?! Wow! A superpowers drug you can just rub onto your skin?</field>
+ <field name="category">default</field>
+ <field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
+ </record>
+ <record id="mail_act_test_meeting" model="mail.activity.type">
+ <field name="name">Meet People</field>
+ <field name="summary">You'd think it would be something you'd have to freebase. Noooooo!</field>
+ <field name="category">default</field>
+ <field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
+ </record>
+ <record id="mail_act_test_call" model="mail.activity.type">
+ <field name="name">Call People</field>
+ <field name="summary">Then throw her in the laundry room, which will hereafter be referred to as "the brig".</field>
+ <field name="category">default</field>
+ <field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
+ </record>
+
+</odoo>
diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py
index 4c1fd408ddf..06d84986fe8 100644
--- a/addons/test_mail/models/test_mail_models.py
+++ b/addons/test_mail/models/test_mail_models.py
@@ -38,6 +38,15 @@ class MailTestActivity(models.Model):
name = fields.Char()
email_from = fields.Char()
+ def action_start(self, action_summary):
+ self.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ summary=action_summary
+ )
+
+ def action_close(self, action_feedback):
+ self.activity_feedback(['test_mail.mail_act_test_todo'], feedback=action_feedback)
+
class MailTestFull(models.Model):
""" This model can be used in tests when complex chatter features are
diff --git a/addons/test_mail/tests/test_mail_activity.py b/addons/test_mail/tests/test_mail_activity.py
index 3a86d86ed4b..e340358a5ee 100644
--- a/addons/test_mail/tests/test_mail_activity.py
+++ b/addons/test_mail/tests/test_mail_activity.py
@@ -14,7 +14,13 @@ class TestMailActivity(BaseFunctionalTest):
@classmethod
def setUpClass(cls):
super(TestMailActivity, cls).setUpClass()
- cls.test_record = cls.env['mail.test.activity'].create({'name': 'Test'})
+ cls.test_record = cls.env['mail.test.activity'].with_context(cls._quick_create_ctx).create({'name': 'Test'})
+ # reset ctx
+ cls.test_record = cls.test_record.with_context(
+ mail_create_nolog=False,
+ mail_create_nosubscribe=False,
+ mail_notrack=False
+ )
def test_activity_flow_employee(self):
with self.sudoAs('ernest'):
@@ -106,3 +112,67 @@ class TestMailActivity(BaseFunctionalTest):
self.assertEqual(reminder.summary, 'Reminder')
reminder.write({'note': 'Holidays', 'summary': 'Summary'})
self.assertEqual(reminder.summary, 'Summary')
+
+ def test_activity_mixin(self):
+ today = date.today()
+ with self.sudoAs('ernest'):
+ self.assertEqual(self.test_record.env.user, self.user_employee)
+
+ # Test various scheduling of activities
+ act1 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ today + relativedelta(days=1),
+ user_id=self.user_admin.id)
+ self.assertEqual(act1.automated, True)
+
+ act_type = self.env.ref('test_mail.mail_act_test_todo')
+ self.assertEqual(self.test_record.activity_summary, act_type.summary)
+ self.assertEqual(self.test_record.activity_state, 'planned')
+ self.assertEqual(self.test_record.activity_user_id, self.user_admin)
+
+ act2 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_meeting',
+ today + relativedelta(days=-1))
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.assertEqual(self.test_record.activity_user_id, self.user_employee)
+
+ act3 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ today + relativedelta(days=3),
+ user_id=self.user_employee.id)
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.assertEqual(self.test_record.activity_user_id, self.user_employee)
+
+ self.assertEqual(self.test_record.activity_ids, act1 | act2 | act3)
+
+ # Perform todo activities for admin
+ self.test_record.activity_feedback(
+ ['test_mail.mail_act_test_todo'],
+ user_id=self.user_admin.id,
+ feedback='Test feedback',)
+ self.assertEqual(self.test_record.activity_ids, act2 | act3)
+
+ # Reschedule all activities, should update the record state
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.test_record.activity_reschedule(
+ ['test_mail.mail_act_test_meeting', 'test_mail.mail_act_test_todo'],
+ date_deadline=today + relativedelta(days=3)
+ )
+ self.assertEqual(self.test_record.activity_state, 'planned')
+
+ # Perform todo activities for remaining people
+ self.test_record.activity_feedback(
+ ['test_mail.mail_act_test_todo'],
+ feedback='Test feedback')
+
+ # Setting activities as done should delete them and post messages
+ self.assertEqual(self.test_record.activity_ids, act2)
+ self.assertEqual(len(self.test_record.message_ids), 2)
+ self.assertEqual(self.test_record.message_ids.mapped('subtype_id'), self.env.ref('mail.mt_activities'))
+
+ # Perform meeting activities
+ self.test_record.activity_unlink(['test_mail.mail_act_test_meeting'])
+
+ # Canceling activities should simply remove them
+ self.assertEqual(self.test_record.activity_ids, self.env['mail.activity'])
+ self.assertEqual(len(self.test_record.message_ids), 2)
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index 183165f5e41..be887fc0173 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -160,16 +160,32 @@ class TestAdvMailPerformance(TransactionCase):
'default_res_model': 'mail.test.activity',
})
- with self.assertQueryCount(admin=26, emp=32): # test_mail only: 26 - 32
+ with self.assertQueryCount(admin=26, emp=33): # test_mail only: 26 - 32
activity = MailActivity.create({
'summary': 'Test Activity',
'res_id': record.id,
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
})
- with self.assertQueryCount(admin=59, emp=88): # test_mail only: 58 - 86
+ with self.assertQueryCount(admin=59, emp=88): # test_mail only: 58 - 87
activity.action_feedback(feedback='Zizisse Done !')
+ @users('admin', 'emp')
+ @warmup
+ @mute_logger('odoo.models.unlink')
+ def test_adv_activity_mixin(self):
+ record = self.env['mail.test.activity'].create({'name': 'Test'})
+
+ with self.assertQueryCount(admin=26, emp=32): # test_mail only: 26 - 32
+ record.action_start('Test Start')
+
+ record.write({'name': 'Dupe write'})
+
+ with self.assertQueryCount(admin=58, emp=88): # test_mail only: 58 - 88
+ record.action_close('Dupe feedback')
+
+ self.assertEqual(record.activity_ids, self.env['mail.activity'])
+
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('admin', 'emp')
@warmup