feat: Ability to schedule onboarding and separation activities (#26738)

* refactor: employee onboarding form clean-up

* feat: ability to schedule onboarding / separation tasks

* feat: skip holidays while setting boarding activity dates

* chore: remove unused child table - Employee Onboarding Activity

* fix: tests

* fix: employee separation test
This commit is contained in:
Rucha Mahabal 2021-08-27 11:12:24 +05:30 committed by GitHub
parent e6799d78ef
commit 71b7c63ec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 129 additions and 322 deletions

View File

@ -5,7 +5,9 @@ import frappe
from frappe import _
from frappe.desk.form import assign_to
from frappe.model.document import Document
from frappe.utils import flt, unique
from frappe.utils import flt, unique, add_days
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
class EmployeeBoardingController(Document):
'''
@ -41,10 +43,14 @@ class EmployeeBoardingController(Document):
def create_task_and_notify_user(self):
# create the task for the given project and assign to the concerned person
holiday_list = self.get_holiday_list()
for activity in self.activities:
if activity.task:
continue
dates = self.get_task_dates(activity, holiday_list)
task = frappe.get_doc({
'doctype': 'Task',
'project': self.project,
@ -52,7 +58,9 @@ class EmployeeBoardingController(Document):
'description': activity.description,
'department': self.department,
'company': self.company,
'task_weight': activity.task_weight
'task_weight': activity.task_weight,
'exp_start_date': dates[0],
'exp_end_date': dates[1]
}).insert(ignore_permissions=True)
activity.db_set('task', task.name)
@ -79,6 +87,36 @@ class EmployeeBoardingController(Document):
if users:
self.assign_task_to_users(task, users)
def get_holiday_list(self):
if self.doctype == 'Employee Separation':
return get_holiday_list_for_employee(self.employee)
else:
if self.employee:
return get_holiday_list_for_employee(self.employee)
else:
if not self.holiday_list:
frappe.throw(_('Please set the Holiday List.'), frappe.MandatoryError)
else:
return self.holiday_list
def get_task_dates(self, activity, holiday_list):
start_date = end_date = None
if activity.begin_on:
start_date = add_days(self.boarding_begins_on, activity.begin_on)
start_date = self.update_if_holiday(start_date, holiday_list)
if activity.duration:
end_date = add_days(self.boarding_begins_on, activity.begin_on + activity.duration)
end_date = self.update_if_holiday(end_date, holiday_list)
return [start_date, end_date]
def update_if_holiday(self, date, holiday_list):
while is_holiday(holiday_list, date):
date = add_days(date, 1)
return date
def assign_task_to_users(self, task, users):
for user in users:
args = {
@ -103,7 +141,8 @@ class EmployeeBoardingController(Document):
@frappe.whitelist()
def get_onboarding_details(parent, parenttype):
return frappe.get_all('Employee Boarding Activity',
fields=['activity_name', 'role', 'user', 'required_for_employee_creation', 'description', 'task_weight'],
fields=['activity_name', 'role', 'user', 'required_for_employee_creation',
'description', 'task_weight', 'begin_on', 'duration'],
filters={'parent': parent, 'parenttype': parenttype},
order_by= 'idx')

View File

@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2018-05-09 05:37:18.439763",
"doctype": "DocType",
"editable_grid": 1,
@ -7,6 +8,8 @@
"activity_name",
"user",
"role",
"begin_on",
"duration",
"column_break_3",
"task",
"task_weight",
@ -16,12 +19,16 @@
],
"fields": [
{
"columns": 3,
"fieldname": "activity_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Activity Name"
"label": "Activity Name",
"reqd": 1
},
{
"columns": 2,
"depends_on": "eval:!doc.role",
"fieldname": "user",
"fieldtype": "Link",
"in_list_view": 1,
@ -29,9 +36,10 @@
"options": "User"
},
{
"columns": 1,
"depends_on": "eval:!doc.user",
"fieldname": "role",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Role",
"options": "Role"
},
@ -67,10 +75,25 @@
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Description"
},
{
"columns": 2,
"fieldname": "duration",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Duration (Days)"
},
{
"columns": 2,
"fieldname": "begin_on",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Begin On (Days)"
}
],
"istable": 1,
"modified": "2019-06-03 19:22:42.965762",
"links": [],
"modified": "2021-07-30 15:55:22.470102",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Boarding Activity",

View File

@ -8,20 +8,24 @@
"field_order": [
"job_applicant",
"job_offer",
"employee_name",
"employee",
"date_of_joining",
"boarding_status",
"notify_users_by_email",
"column_break_7",
"employee_onboarding_template",
"column_break_7",
"company",
"boarding_status",
"project",
"details_section",
"employee",
"employee_name",
"department",
"designation",
"employee_grade",
"project",
"holiday_list",
"column_break_13",
"date_of_joining",
"boarding_begins_on",
"table_for_activity",
"activities",
"notify_users_by_email",
"amended_from"
],
"fields": [
@ -58,7 +62,8 @@
"fieldname": "date_of_joining",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date of Joining"
"label": "Date of Joining",
"reqd": 1
},
{
"allow_on_submit": 1,
@ -90,7 +95,8 @@
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
"options": "Company",
"reqd": 1
},
{
"fieldname": "department",
@ -121,7 +127,8 @@
},
{
"fieldname": "table_for_activity",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Onboarding Activities"
},
{
"allow_on_submit": 1,
@ -138,11 +145,32 @@
"options": "Employee Onboarding",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "details_section",
"fieldtype": "Section Break",
"label": "Employee Details"
},
{
"fieldname": "column_break_13",
"fieldtype": "Column Break"
},
{
"fieldname": "boarding_begins_on",
"fieldtype": "Date",
"label": "Onboarding Begins On",
"reqd": 1
},
{
"fieldname": "holiday_list",
"fieldtype": "Link",
"label": "Holiday List",
"options": "Holiday List"
}
],
"is_submittable": 1,
"links": [],
"modified": "2021-06-03 18:01:51.097927",
"modified": "2021-07-30 14:55:04.560683",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Onboarding",

View File

@ -5,8 +5,9 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import nowdate
from frappe.utils import getdate
from erpnext.hr.doctype.employee_onboarding.employee_onboarding import make_employee
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
from erpnext.hr.doctype.employee_onboarding.employee_onboarding import IncompleteTaskError
from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer
@ -46,7 +47,7 @@ class TestEmployeeOnboarding(unittest.TestCase):
onboarding.reload()
employee = make_employee(onboarding.name)
employee.first_name = employee.employee_name
employee.date_of_joining = nowdate()
employee.date_of_joining = getdate()
employee.date_of_birth = '1990-05-08'
employee.gender = 'Female'
employee.insert()
@ -82,11 +83,14 @@ def get_job_offer(applicant_name):
def create_employee_onboarding():
applicant = get_job_applicant()
job_offer = get_job_offer(applicant.name)
holiday_list = make_holiday_list()
onboarding = frappe.new_doc('Employee Onboarding')
onboarding.job_applicant = applicant.name
onboarding.job_offer = job_offer.name
onboarding.date_of_joining = onboarding.boarding_begins_on = getdate()
onboarding.company = '_Test Company'
onboarding.holiday_list = holiday_list
onboarding.designation = 'Researcher'
onboarding.append('activities', {
'activity_name': 'Assign ID Card',

View File

@ -1,290 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-05-09 05:37:18.439763",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "activity_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Activity Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "role",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Role",
"length": 0,
"no_copy": 0,
"options": "Role",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.parenttype == \"Employee Onboarding\"",
"fieldname": "completed",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Completed",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "required_for_employee_creation",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Required for Employee Creation",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-05-09 06:15:41.768236",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Onboarding Activity",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class EmployeeOnboardingActivity(Document):
pass

View File

@ -15,6 +15,7 @@
"company",
"boarding_status",
"resignation_letter_date",
"boarding_begins_on",
"project",
"table_for_activity",
"employee_separation_template",
@ -144,11 +145,17 @@
"options": "Employee Separation",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "boarding_begins_on",
"fieldtype": "Date",
"label": "Separation Begins On",
"reqd": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2021-06-03 18:02:54.007313",
"modified": "2021-07-30 14:03:51.218791",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Separation",

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import getdate
import unittest
test_dependencies = ['Employee Onboarding']
@ -34,9 +35,10 @@ class TestEmployeeSeparation(unittest.TestCase):
doc.delete()
def create_employee_separation():
employee = frappe.db.get_value('Employee', {'status': 'Active'})
employee = frappe.db.get_value('Employee', {'status': 'Active', 'company': '_Test Company'})
separation = frappe.new_doc('Employee Separation')
separation.employee = employee
separation.boarding_begins_on = getdate()
separation.company = '_Test Company'
separation.append('activities', {
'activity_name': 'Deactivate Employee',

View File

@ -828,7 +828,8 @@ def setup_test():
def make_holiday_list():
fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
holiday_list = frappe.db.exists("Holiday List", "Salary Slip Test Holiday List")
if not holiday_list:
holiday_list = frappe.get_doc({
"doctype": "Holiday List",
"holiday_list_name": "Salary Slip Test Holiday List",
@ -838,3 +839,6 @@ def make_holiday_list():
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
holiday_list = holiday_list.name
return holiday_list