[IMP] mail: Add 'Next Activity' in server action type
Purpose ======= As a server action could add followers on a record, here the goal is to be able to add a next activity on a record. Specification ============= Based on the configured trigger, we can add a next activity on a record by specifying : - The activity type - A summary - A note - A due date in x days/weeks/months - A responsible
This commit is contained in:
committed by
Yannick Tivisse
parent
e4381d9f1b
commit
7309981426
@@ -1,6 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
@@ -12,7 +15,8 @@ class ServerActions(models.Model):
|
||||
|
||||
state = fields.Selection(selection_add=[
|
||||
('email', 'Send Email'),
|
||||
('followers', 'Add Followers')
|
||||
('followers', 'Add Followers'),
|
||||
('next_activity', 'Create Next Activity'),
|
||||
])
|
||||
# Followers
|
||||
partner_ids = fields.Many2many('res.partner', string='Add Followers')
|
||||
@@ -22,6 +26,29 @@ class ServerActions(models.Model):
|
||||
'mail.template', 'Email Template', ondelete='set null',
|
||||
domain="[('model_id', '=', model_id)]",
|
||||
)
|
||||
# Next Activity
|
||||
activity_type_id = fields.Many2one(
|
||||
'mail.activity.type', string='Activity',
|
||||
domain="['|', ('res_model_id', '=', False), ('res_model_id', '=', model_id)]")
|
||||
activity_summary = fields.Char('Summary')
|
||||
activity_note = fields.Html('Note')
|
||||
activity_date_deadline_range = fields.Integer(string='Due Date In')
|
||||
activity_date_deadline_range_type = fields.Selection([
|
||||
('days', 'Days'),
|
||||
('weeks', 'Weeks'),
|
||||
('months', 'Months'),
|
||||
], string='Due type', default='days')
|
||||
activity_user_type = fields.Selection([
|
||||
('specific', 'Specific User'),
|
||||
('generic', 'Generic User From Record')], default="specific", required=True,
|
||||
help="Use 'Specific User' to always assign the same user on the next activity. Use 'Generic User From Record' to specify the field name of the user to choose on the record.")
|
||||
activity_user_id = fields.Many2one('res.users', string='Responsible')
|
||||
activity_user_field_name = fields.Char('User field name', help="Technical name of the user on the record", default="user_id")
|
||||
|
||||
@api.onchange('activity_date_deadline_range')
|
||||
def _onchange_activity_date_deadline_range(self):
|
||||
if self.activity_date_deadline_range < 0:
|
||||
raise UserError(_("The 'Due Date In' value can't be negative."))
|
||||
|
||||
@api.onchange('template_id')
|
||||
def on_change_template_id(self):
|
||||
@@ -35,6 +62,12 @@ class ServerActions(models.Model):
|
||||
if action.state == 'followers' and not action.model_id.is_mail_thread:
|
||||
raise ValidationError(_("Add Followers can only be done on a mail thread model"))
|
||||
|
||||
@api.constrains('state', 'model_id')
|
||||
def _check_activity_mixin(self):
|
||||
for action in self:
|
||||
if action.state == 'next_activity' and not issubclass(self.pool[action.model_id.model], self.pool['mail.thread']):
|
||||
raise ValidationError(_("A next activity can only be planned on models that use the chatter"))
|
||||
|
||||
@api.model
|
||||
def run_action_followers_multi(self, action, eval_context=None):
|
||||
Model = self.env[action.model_id.model]
|
||||
@@ -51,6 +84,29 @@ class ServerActions(models.Model):
|
||||
action.template_id.send_mail(self._context.get('active_id'), force_send=False, raise_exception=False)
|
||||
return False
|
||||
|
||||
@api.model
|
||||
def run_action_next_activity(self, action, eval_context=None):
|
||||
if not action.activity_type_id or not self._context.get('active_id'):
|
||||
return False
|
||||
|
||||
records = self.env[action.model_id.model].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||
vals = {
|
||||
'summary': action.activity_summary or '',
|
||||
'note': action.activity_note or '',
|
||||
'activity_type_id': action.activity_type_id.id,
|
||||
}
|
||||
if action.activity_date_deadline_range > 0:
|
||||
vals['date_deadline'] = date.today() + relativedelta(**{action.activity_date_deadline_range_type: action.activity_date_deadline_range})
|
||||
for record in records:
|
||||
if action.activity_user_type == 'specific':
|
||||
user = action.activity_user_id
|
||||
elif action.activity_user_type == 'generic' and action.activity_user_field_name in record:
|
||||
user = record[action.activity_user_field_name]
|
||||
if user:
|
||||
vals['user_id'] = user.id
|
||||
record.activity_schedule(**vals)
|
||||
return False
|
||||
|
||||
@api.model
|
||||
def _get_eval_context(self, action=None):
|
||||
""" Override the method giving the evaluation context but also the
|
||||
|
||||
@@ -7,6 +7,27 @@
|
||||
<field name="model">ir.actions.server</field>
|
||||
<field name="inherit_id" ref="base.view_server_action_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<notebook position="inside">
|
||||
<page string="Activity" name="next_activity" autofocus="autofocus" attrs="{'invisible': [('state', '!=', 'next_activity')]}">
|
||||
<group>
|
||||
<group>
|
||||
<field name="activity_type_id" options="{'no_create': True, 'no_open': True}" attrs="{'required': [('state', '=', 'next_activity')]}"/>
|
||||
<field name="activity_summary" placeholder="e.g. Discuss proposal"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="activity_date_deadline_range"/>
|
||||
<div class="o_row">
|
||||
<field name="activity_date_deadline_range"/>
|
||||
<field name="activity_date_deadline_range_type"/>
|
||||
</div>
|
||||
<field name="activity_user_type"/>
|
||||
<field name="activity_user_field_name" attrs="{'invisible': [('activity_user_type', '=', 'specific')], 'required': [('activity_user_type', '=', 'generic')]}"/>
|
||||
<field name="activity_user_id" attrs="{'invisible': [('activity_user_type', '=', 'generic')], 'required': [('activity_user_type', '=', 'specific')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="activity_note" placeholder="Log a note..."/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="link_field_id" position="after">
|
||||
<field name="partner_ids" widget="many2many_tags"
|
||||
attrs="{'invisible': [('state', '!=', 'followers')]}"/>
|
||||
|
||||
@@ -34,3 +34,16 @@ class TestServerActionsEmail(TestServerActionsBase):
|
||||
self.action.with_context(self.context).run()
|
||||
self.assertEqual(self.test_partner.message_partner_ids, self.env.ref('base.partner_root') | self.env.ref('base.partner_demo'))
|
||||
self.assertEqual(self.test_partner.message_channel_ids, self.env.ref('mail.channel_all_employees'))
|
||||
|
||||
def test_action_next_activity(self):
|
||||
self.action.write({
|
||||
'state': 'next_activity',
|
||||
'activity_user_type': 'specific',
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_meeting').id,
|
||||
'activity_summary': 'TestNew',
|
||||
})
|
||||
before_count = self.env['mail.activity'].search_count([])
|
||||
run_res = self.action.with_context(self.context).run()
|
||||
self.assertFalse(run_res, 'ir_actions_server: create next activity action correctly finished should return False')
|
||||
self.assertEqual(self.env['mail.activity'].search_count([]), before_count + 1)
|
||||
self.assertEqual(self.env['mail.activity'].search_count([('summary', '=', 'TestNew')]), 1)
|
||||
|
||||
Reference in New Issue
Block a user