diff --git a/addons/hr/__manifest__.py b/addons/hr/__manifest__.py
index 82bbcf4996c..bcbb074f591 100644
--- a/addons/hr/__manifest__.py
+++ b/addons/hr/__manifest__.py
@@ -37,6 +37,7 @@
'views/mail_channel_views.xml',
'views/res_users.xml',
'views/res_partner_views.xml',
+ 'views/hr_templates.xml',
'data/hr_data.xml',
],
'demo': [
diff --git a/addons/hr/models/__init__.py b/addons/hr/models/__init__.py
index b891bf1cecf..7e1a795a8ff 100644
--- a/addons/hr/models/__init__.py
+++ b/addons/hr/models/__init__.py
@@ -11,6 +11,7 @@ from . import hr_departure_reason
from . import hr_job
from . import hr_plan
from . import hr_plan_activity_type
+from . import hr_plan_employee_activity
from . import hr_work_location
from . import mail_alias
from . import mail_channel
diff --git a/addons/hr/models/hr_plan_employee_activity.py b/addons/hr/models/hr_plan_employee_activity.py
new file mode 100644
index 00000000000..ba4b1599809
--- /dev/null
+++ b/addons/hr/models/hr_plan_employee_activity.py
@@ -0,0 +1,32 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class HrPlanEmployeeActivity(models.Model):
+ _name = 'hr.plan.employee.activity'
+ _description = 'Plan'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _mail_post_access = 'read'
+ _rec_name = 'employee_id'
+
+ employee_id = fields.Many2one('hr.employee', required=True, ondelete='cascade')
+ department_id = fields.Many2one(related='employee_id.department_id')
+ image_128 = fields.Image(related='employee_id.image_128')
+ company_id = fields.Many2one(related='employee_id.company_id')
+ user_id = fields.Many2one(related='employee_id.user_id')
+ summary = fields.Html(compute='_compute_summary')
+
+ @api.depends('activity_ids.summary')
+ def _compute_summary(self):
+ for plan in self:
+ plan.summary = self.env['ir.ui.view']._render_template('hr.hr_employee_plan_activity_summary', {
+ 'activity_ids': plan.activity_ids,
+ })
+
+ @api.autovacuum
+ def _gc_employee_plan_activity(self):
+ no_activities = self.env['hr.plan.employee.activity'].search(
+ [('activity_ids', '=', False)]
+ )
+ no_activities.unlink()
diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml
index 0992af253a6..d8ac61cc9bd 100644
--- a/addons/hr/security/hr_security.xml
+++ b/addons/hr/security/hr_security.xml
@@ -60,5 +60,17 @@
['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
+
+
+ Plan Employee Activity: multi-company rule
+
+ ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
+
+
+
+ Plan Employee Activity: own employee or subordinates
+
+ ['|', ('user_id', '=', user.id), ('activity_ids.user_id', '=', user.id)]
+
diff --git a/addons/hr/security/ir.model.access.csv b/addons/hr/security/ir.model.access.csv
index 0f662b3901c..9affc1c6bea 100644
--- a/addons/hr/security/ir.model.access.csv
+++ b/addons/hr/security/ir.model.access.csv
@@ -18,3 +18,4 @@ access_hr_work_location_user,access_hr_work_location_user,model_hr_work_location
access_hr_work_location_manager,access_hr_work_location_manager,model_hr_work_location,group_hr_manager,1,1,1,1
access_hr_departure_reason,access_hr_departure_reason_user,model_hr_departure_reason,group_hr_user,1,1,1,1
access_hr_contract_type_manager,hr.contract.type.manager,model_hr_contract_type,hr.group_hr_user,1,1,1,1
+access_hr_plan_employee_activity,hr.plan.employee.activity,model_hr_plan_employee_activity,base.group_user,1,0,0,0
diff --git a/addons/hr/views/hr_plan_views.xml b/addons/hr/views/hr_plan_views.xml
index 2c0b7c35bd8..50d74c41067 100644
--- a/addons/hr/views/hr_plan_views.xml
+++ b/addons/hr/views/hr_plan_views.xml
@@ -121,5 +121,63 @@
+
+ hr.plan.employee.activity
+
+
+
+
+
+
+
+
+
+ Next Activity:
+
+
+
+
+
+
+
+
+
+
+ hr.plan.employee.activity
+
+
+
+
+
+
+
+
+
+
+
+ hr.plan.employee.activity
+
+
+
+
diff --git a/addons/hr/views/hr_templates.xml b/addons/hr/views/hr_templates.xml
new file mode 100644
index 00000000000..0065031607d
--- /dev/null
+++ b/addons/hr/views/hr_templates.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ ()
+
+
+
+
+
diff --git a/addons/hr/wizard/hr_plan_wizard.py b/addons/hr/wizard/hr_plan_wizard.py
index 0cbf893f562..82ce4f4e1fc 100644
--- a/addons/hr/wizard/hr_plan_wizard.py
+++ b/addons/hr/wizard/hr_plan_wizard.py
@@ -74,17 +74,24 @@ class HrPlanWizard(models.TransientModel):
activities = set()
for activity_type in self._get_activities_to_schedule():
responsible = activity_type.get_responsible_id(employee)['responsible']
- if self.env['hr.employee'].with_user(responsible).check_access_rights('read', raise_exception=False):
- date_deadline = self.env['mail.activity']._calculate_date_deadline(activity_type.activity_type_id)
- employee.activity_schedule(
- activity_type_id=activity_type.activity_type_id.id,
- summary=activity_type.summary,
- note=activity_type.note,
- user_id=responsible.id,
- date_deadline=date_deadline
- )
- activity = _('%(activity)s, assigned to %(name)s, due on the %(deadline)s', activity=activity_type.summary, name=responsible.name, deadline=date_deadline)
- activities.add(activity)
+ record = employee
+ if not self.env['hr.employee'].with_user(responsible).check_access_rights('read', raise_exception=False):
+ record = self.env['hr.plan.employee.activity'].sudo().search([('employee_id', '=', employee.id)], limit=1)
+ if not record:
+ record = self.env['hr.plan.employee.activity'].sudo().create({
+ 'employee_id': employee.id,
+ })
+
+ date_deadline = self.env['mail.activity']._calculate_date_deadline(activity_type.activity_type_id)
+ record.activity_schedule(
+ activity_type_id=activity_type.activity_type_id.id,
+ summary=activity_type.summary,
+ note=activity_type.note,
+ user_id=responsible.id,
+ date_deadline=date_deadline
+ )
+ activity = _('%(activity)s, assigned to %(name)s, due on the %(deadline)s', activity=activity_type.summary, name=responsible.name, deadline=date_deadline)
+ activities.add(activity)
if activities:
body += '