[IMP] hr_appraisal: Improve employee archiving flow
PURPOSE
=======
When you close a payroll (employees), there are often a lot of document
linked. Archiving an employee should archive the contract, cancel future
leaves, archive the private address
Specification
=============
Add departure date (hr):
- Add a Date field to both hr.departure.wizard and hr.employee. In
the wizard, the field is required.
- In toggle_active() method in hr.employee, set departure_date to
false when unarchive the employee.
- If user has a current running contract, a user error will raise if
user enter a departure date earlier than the start date of the
contract.
Add checkbox to set a closing date on hr.contract (hr.contract):
- In the hr.departure.wizard, set the departure date to be the end
date of runing contract. Set the states of all draft contracts to
"cancel".
Add checkbox to free car (hr.fleet):
- In the hr.departure.wizard, set end_date to
fleet.vehicle.assignation.log, if there is no end_date or end_date >
departure_date
- Go through fleet.vehicle, find records with dirver_id to be the
employee, set it to False.
Add checkbox to archive private address (hr):
- when the private address not link to a internel user, set
employee.address_home_id.active to Flase
- unarchive it after the employee unarchived
Add checkbox to cancel future appraisals (hr.appraisal)
- find all appraisals link to the employee and state in
['new', 'pending'], set their state to 'cancel'.
Add checkbox to cancel future leaves (hr.holidays):
- only consider leaves are not in state ['refuse', 'cancel'],
find leaves with to_date > departure_date, set their state to
'cancel'.
PR #42526
Task 2153106
Related: odoo/enterprise#7471
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
79ce907f66
commit
32fc38ef58
@@ -114,6 +114,7 @@ class HrEmployeePrivate(models.Model):
|
||||
('retired', 'Retired')
|
||||
], string="Departure Reason", groups="hr.group_hr_user", copy=False, tracking=True)
|
||||
departure_description = fields.Text(string="Additional Information", groups="hr.group_hr_user", copy=False, tracking=True)
|
||||
departure_date = fields.Date(string="Departure Date", groups="hr.group_hr_user", copy=False, tracking=True)
|
||||
message_main_attachment_id = fields.Many2one(groups="hr.group_hr_user")
|
||||
|
||||
_sql_constraints = [
|
||||
@@ -273,10 +274,14 @@ class HrEmployeePrivate(models.Model):
|
||||
|
||||
def toggle_active(self):
|
||||
res = super(HrEmployeePrivate, self).toggle_active()
|
||||
self.filtered(lambda employee: employee.active).write({
|
||||
unarchived_employees = self.filtered(lambda employee: employee.active)
|
||||
unarchived_employees.write({
|
||||
'departure_reason': False,
|
||||
'departure_description': False,
|
||||
'departure_date': False
|
||||
})
|
||||
archived_addresses = unarchived_employees.mapped('address_home_id').filtered(lambda addr: not addr.active)
|
||||
archived_addresses.toggle_active()
|
||||
if len(self) == 1 and not self.active:
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
<group name="departure" string="Departure" attrs="{'invisible': [('active', '=', True)]}">
|
||||
<field name="departure_reason"/>
|
||||
<field name="departure_description"/>
|
||||
<field name="departure_date"/>
|
||||
</group>
|
||||
<group string="Schedule" groups="base.group_no_one">
|
||||
<field name="resource_calendar_id" required="1"/>
|
||||
|
||||
@@ -22,22 +22,18 @@ class HrDepartureWizard(models.TransientModel):
|
||||
('retired', 'Retired')
|
||||
], string="Departure Reason", default="fired")
|
||||
departure_description = fields.Text(string="Additional Information")
|
||||
plan_id = fields.Many2one('hr.plan', default=lambda self: self.env['hr.plan'].search([], limit=1))
|
||||
departure_date = fields.Date(string="Departure Date", required=True, default=fields.Date.today)
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee', required=True)
|
||||
archive_private_address = fields.Boolean('Archive Private Address', default=True)
|
||||
|
||||
def action_register_departure(self):
|
||||
employee = self.employee_id
|
||||
employee.departure_reason = self.departure_reason
|
||||
employee.departure_description = self.departure_description
|
||||
employee.departure_date = self.departure_date
|
||||
|
||||
if not employee.user_id.partner_id:
|
||||
return
|
||||
|
||||
for activity_type in self.plan_id.plan_activity_type_ids:
|
||||
self.env['mail.activity'].create({
|
||||
'res_id': employee.user_id.partner_id.id,
|
||||
'res_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
'activity_type_id': activity_type.activity_type_id.id,
|
||||
'summary': activity_type.summary,
|
||||
'user_id': activity_type.get_responsible_id(employee).id,
|
||||
})
|
||||
if self.archive_private_address:
|
||||
# ignore contact links to internal users
|
||||
private_address = employee.address_home_id
|
||||
if private_address and private_address.active and not self.env['res.users'].search([('partner_id', '=', private_address.id)]):
|
||||
private_address.toggle_active()
|
||||
|
||||
@@ -7,16 +7,19 @@
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="plan_id"/>
|
||||
<group id="info" string="Departure Info">
|
||||
<field name="employee_id" invisible="1"/>
|
||||
<field name="departure_reason"/>
|
||||
<field name="departure_description"/>
|
||||
<field name="archive_private_address"/>
|
||||
</group>
|
||||
<group id="date" string="Departure Date">
|
||||
<field name="departure_date" />
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_register_departure" string="Save" type="object" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
<button string="Only Employee" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import wizard
|
||||
|
||||
@@ -24,6 +24,7 @@ You can assign several contracts per employee.
|
||||
'data/hr_contract_data.xml',
|
||||
'views/hr_contract_views.xml',
|
||||
'views/assets.xml',
|
||||
'wizard/hr_departure_wizard_views.xml',
|
||||
],
|
||||
'demo': ['data/hr_contract_demo.xml'],
|
||||
'installable': True,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_departure_wizard
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class HrDepartureWizard(models.TransientModel):
|
||||
_inherit = 'hr.departure.wizard'
|
||||
|
||||
set_date_end = fields.Boolean(string="Set Contract End Date", default=True)
|
||||
|
||||
def action_register_departure(self):
|
||||
"""If set_date_end is checked, set the departure date as the end date to current running contract,
|
||||
and cancel all draft contracts"""
|
||||
current_contract = self.employee_id.contract_id
|
||||
if current_contract and current_contract.date_start > self.departure_date:
|
||||
raise UserError(_("Departure date can't be earlier than the start date of current contract."))
|
||||
|
||||
super(HrDepartureWizard, self).action_register_departure()
|
||||
if self.set_date_end:
|
||||
self.employee_id.contract_ids.filtered(lambda c: c.state == 'draft').write({'state': 'cancel'})
|
||||
if current_contract:
|
||||
self.employee_id.contract_id.write({'date_end': self.departure_date})
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="hr_departure_wizard_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.departure.wizard.view.form.extend</field>
|
||||
<field name="model">hr.departure.wizard</field>
|
||||
<field name="inherit_id" ref="hr.hr_departure_wizard_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='departure_date']" position="replace">
|
||||
<field name="departure_date" string="Contract End Date"/>
|
||||
</xpath>
|
||||
<xpath expr="//group[@id='date']" position="inside">
|
||||
<field name="set_date_end" string="Set end date to current contract"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
'data': [
|
||||
'views/employee_views.xml',
|
||||
'views/fleet_vehicle_views.xml',
|
||||
'wizard/hr_departure_wizard_views.xml'
|
||||
],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_departure_wizard
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class HrDepartureWizard(models.TransientModel):
|
||||
_inherit = 'hr.departure.wizard'
|
||||
|
||||
release_campany_car = fields.Boolean("Release Company Car", default=True)
|
||||
|
||||
def action_register_departure(self):
|
||||
super(HrDepartureWizard, self).action_register_departure()
|
||||
if self.release_campany_car:
|
||||
self._free_campany_car()
|
||||
|
||||
def _free_campany_car(self):
|
||||
"""Find all fleet.vehichle.assignation.log records that link to the employee, if there is no
|
||||
end date or end date > departure date, update the date. Also check fleet.vehicle to see if
|
||||
there is any record with its dirver_id to be the employee, set them to False."""
|
||||
drivers = self.employee_id.user_id.partner_id | self.employee_id.sudo().address_home_id
|
||||
assignations = self.env['fleet.vehicle.assignation.log'].search([('driver_id', 'in', drivers.ids)])
|
||||
for assignation in assignations:
|
||||
if self.departure_date and (not assignation.date_end or assignation.date_end > self.departure_date):
|
||||
assignation.write({'date_end': self.departure_date})
|
||||
cars = self.env['fleet.vehicle'].search([('driver_id', 'in', drivers.ids)])
|
||||
cars.write({'driver_id': False})
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="hr_departure_wizard_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.departure.wizard.view.form.extend2</field>
|
||||
<field name="model">hr.departure.wizard</field>
|
||||
<field name="inherit_id" ref="hr.hr_departure_wizard_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='archive_private_address']" position="after">
|
||||
<field name="release_campany_car"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -40,6 +40,7 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p
|
||||
'views/mail_activity_views.xml',
|
||||
|
||||
'wizard/hr_holidays_summary_employees_views.xml',
|
||||
'wizard/hr_departure_wizard_views.xml',
|
||||
|
||||
'report/hr_holidays_templates.xml',
|
||||
'report/hr_holidays_reports.xml',
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_holidays_summary_employees
|
||||
from . import hr_departure_wizard
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class HrDepartureWizard(models.TransientModel):
|
||||
_inherit = 'hr.departure.wizard'
|
||||
|
||||
cancel_leaves = fields.Boolean("Cancel Future Leaves", default=True)
|
||||
|
||||
def action_register_departure(self):
|
||||
super(HrDepartureWizard, self).action_register_departure()
|
||||
if self.cancel_leaves:
|
||||
future_leaves = self.env['hr.leave'].search([('employee_id', '=', self.employee_id.id),
|
||||
('date_to', '>', self.departure_date),
|
||||
('state', 'not in', ['cancel', 'refuse'])])
|
||||
future_leaves.write({'state': 'cancel'})
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="hr_departure_wizard_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.departure.wizard.view.form.extend3</field>
|
||||
<field name="model">hr.departure.wizard</field>
|
||||
<field name="inherit_id" ref="hr.hr_departure_wizard_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@id='date']" position="inside">
|
||||
<field name="cancel_leaves" string="Cancel all time off after this date"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user