Files
odoo_source/addons/hr/models/hr_plan.py
T
William Braeckman 864595901e [IMP] hr,hr_payroll: plan automation on triggers
Automate activites linked to plan on certain triggers.
To automate onboarding and offboarding processes, we can now define
plans that will activate upon triggers, such as employee creation,
departure (archive), contract start or contract end.
Manual plans are still possible.

Upon activation of a plan, a message will be added to the employee's
chatter with the name of the plan.

More options have been added related to when to schedule the plan's
activities.

The blocking mechanism has been removed (the plan would not launch if an
activity could not be started, due to lack of information), failed
activities will now be logged into the employee's chatter.

The list and kanban employee views have also been updated to include
info such as first contract date and activity information.

Task ID: 2489095

closes odoo/odoo#68451

Related: odoo/enterprise#17343
Related: odoo/upgrade#2314
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
2021-06-03 12:09:32 +00:00

153 lines
6.0 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class HrPlanActivityType(models.Model):
_name = 'hr.plan.activity.type'
_description = 'Plan activity type'
_rec_name = 'summary'
_sql_constraints = [
(
'check_deadline_days', 'CHECK (COALESCE(deadline_days) >= 0)',
'Days deadline must be positive.'
),
]
activity_type_id = fields.Many2one(
'mail.activity.type', 'Activity Type',
default=lambda self: self.env.ref('mail.mail_activity_data_todo'),
domain=lambda self: ['|', ('res_model_id', '=', False), ('res_model_id', '=', self.env['ir.model']._get('hr.employee').id)],
ondelete='restrict'
)
summary = fields.Char('Summary', compute="_compute_default_summary", store=True, readonly=False)
responsible = fields.Selection([
('coach', 'Coach'),
('manager', 'Manager'),
('employee', 'Employee'),
('other', 'Other')], default='employee', string='Responsible', required=True)
responsible_id = fields.Many2one('res.users', 'Name', help='Specific responsible of activity if not linked to the employee.')
note = fields.Html('Note')
deadline_type = fields.Selection(
[
('default', 'Default value'),
('plan_active', "At plan's activation"),
('trigger_offset', 'Days after activation trigger'),
],
string='Activity Deadline',
default='default',
required=True,
)
deadline_days = fields.Integer(string='Days Deadline')
company_id = fields.Many2one(
'res.company',
string='Company',
default=lambda self: self.env.company,
)
@api.depends('activity_type_id')
def _compute_default_summary(self):
for plan_type in self:
if not plan_type.summary and plan_type.activity_type_id and plan_type.activity_type_id.summary:
plan_type.summary = plan_type.activity_type_id.summary
def get_responsible_id(self, employee):
if self.responsible == 'coach':
if not employee.coach_id:
raise UserError(_('Coach of employee %s is not set.', employee.name))
responsible = employee.coach_id.user_id
if not responsible:
raise UserError(_('User of coach of employee %s is not set.', employee.name))
elif self.responsible == 'manager':
if not employee.parent_id:
raise UserError(_('Manager of employee %s is not set.', employee.name))
responsible = employee.parent_id.user_id
if not responsible:
raise UserError(_('User of manager of employee %s is not set.', employee.name))
elif self.responsible == 'employee':
responsible = employee.user_id
if not responsible:
raise UserError(_('User linked to employee %s is required.', employee.name))
elif self.responsible == 'other':
responsible = self.responsible_id
if not responsible:
raise UserError(_('No specific user given on activity %s.', self.activity_type_id.name))
return responsible
class HrPlan(models.Model):
_name = 'hr.plan'
_description = 'plan'
name = fields.Char('Name', required=True)
plan_activity_type_ids = fields.Many2many('hr.plan.activity.type', string='Activities')
active = fields.Boolean(default=True)
plan_type = fields.Selection(
[
('onboarding', 'Onboarding'),
('offboarding', 'Offboarding'),
('other', 'Other'),
], string='Type', default='onboarding', required=True,
)
trigger_onboarding = fields.Selection(
[
('manual', 'Manual'),
('employee_creation', 'Employee Creation'),
], compute='_compute_triggers', inverse='_inverse_triggers',
required=True, readonly=False,
)
trigger_offboarding = fields.Selection(
[
('manual', 'Manual'),
('employee_archive', 'Archived Employee'),
], compute='_compute_triggers', inverse='_inverse_triggers',
required=True, readonly=False,
)
trigger_other = fields.Selection(
[
('manual', 'Manual'),
], compute='_compute_triggers', inverse='_inverse_triggers',
required=True, readonly=False,
)
trigger = fields.Char(default='manual', compute='_compute_trigger', store=True)
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company)
@api.depends('trigger')
def _compute_triggers(self):
trigger_types = {'trigger_onboarding', 'trigger_offboarding', 'trigger_other'}
type_to_trigger = {
'onboarding': 'trigger_onboarding',
'offboarding': 'trigger_offboarding',
'other': 'trigger_other',
}
for record in self:
#trigger for active
record[type_to_trigger[record.plan_type]] = record.trigger or 'manual'
#'manual' for all others
for disabled_trigger in trigger_types - {type_to_trigger[record.plan_type]}:
record[disabled_trigger] = 'manual'
def _inverse_triggers(self):
type_to_trigger = {
'onboarding': 'trigger_onboarding',
'offboarding': 'trigger_offboarding',
'other': 'trigger_other',
}
for record in self:
# or 'manual' required is for trigger_other since it can not be changed it's always False here
record.trigger = record[type_to_trigger[record.plan_type]] or 'manual'
@api.depends('plan_type')
def _compute_trigger(self):
# In case only plan_type changes
type_to_trigger = {
'onboarding': 'trigger_onboarding',
'offboarding': 'trigger_offboarding',
'other': 'trigger_other',
}
for record in self:
record.trigger = record[type_to_trigger[record.plan_type]] or 'manual'