fix: boarding status in Employee Onboarding and Separation

- make boarding status read-only as its dependent on project

- update boarding status in onboarding/separation on project update

- update tests to check status changes
This commit is contained in:
Rucha Mahabal
2021-06-03 18:07:00 +05:30
parent 3d5e15284b
commit 67519d6b79
8 changed files with 130 additions and 135 deletions

View File

@@ -50,21 +50,6 @@ frappe.ui.form.on('Employee Onboarding', {
}, __('Create')); }, __('Create'));
frm.page.set_inner_btn_group_as_primary(__('Create')); frm.page.set_inner_btn_group_as_primary(__('Create'));
} }
if (frm.doc.docstatus === 1 && frm.doc.project) {
frappe.call({
method: "erpnext.hr.utils.get_boarding_status",
args: {
"project": frm.doc.project
},
callback: function(r) {
if (r.message) {
frm.set_value('boarding_status', r.message);
}
refresh_field("boarding_status");
}
});
}
}, },
employee_onboarding_template: function(frm) { employee_onboarding_template: function(frm) {

View File

@@ -30,18 +30,14 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Job Applicant", "label": "Job Applicant",
"options": "Job Applicant", "options": "Job Applicant",
"reqd": 1, "reqd": 1
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "job_offer", "fieldname": "job_offer",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Job Offer", "label": "Job Offer",
"options": "Job Offer", "options": "Job Offer",
"reqd": 1, "reqd": 1
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fetch_from": "job_applicant.applicant_name", "fetch_from": "job_applicant.applicant_name",
@@ -49,116 +45,90 @@
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"label": "Employee Name", "label": "Employee Name",
"reqd": 1, "reqd": 1
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "employee", "fieldname": "employee",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Employee", "label": "Employee",
"options": "Employee", "options": "Employee",
"read_only": 1, "read_only": 1
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "date_of_joining", "fieldname": "date_of_joining",
"fieldtype": "Date", "fieldtype": "Date",
"in_list_view": 1, "in_list_view": 1,
"label": "Date of Joining", "label": "Date of Joining"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "Pending",
"fieldname": "boarding_status", "fieldname": "boarding_status",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"options": "\nPending\nIn Process\nCompleted", "options": "Pending\nIn Process\nCompleted",
"show_days": 1, "read_only": 1
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "0", "default": "0",
"fieldname": "notify_users_by_email", "fieldname": "notify_users_by_email",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Notify users by email", "label": "Notify users by email"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "column_break_7", "fieldname": "column_break_7",
"fieldtype": "Column Break", "fieldtype": "Column Break"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "employee_onboarding_template", "fieldname": "employee_onboarding_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Employee Onboarding Template", "label": "Employee Onboarding Template",
"options": "Employee Onboarding Template", "options": "Employee Onboarding Template"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Company", "label": "Company",
"options": "Company", "options": "Company"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "department", "fieldname": "department",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Department", "label": "Department",
"options": "Department", "options": "Department"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "designation", "fieldname": "designation",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Designation", "label": "Designation",
"options": "Designation", "options": "Designation"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "employee_grade", "fieldname": "employee_grade",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Employee Grade", "label": "Employee Grade",
"options": "Employee Grade", "options": "Employee Grade"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
"options": "Project", "options": "Project",
"read_only": 1, "read_only": 1
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "table_for_activity", "fieldname": "table_for_activity",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "activities", "fieldname": "activities",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Activities", "label": "Activities",
"options": "Employee Boarding Activity", "options": "Employee Boarding Activity"
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "amended_from", "fieldname": "amended_from",
@@ -167,14 +137,12 @@
"no_copy": 1, "no_copy": 1,
"options": "Employee Onboarding", "options": "Employee Onboarding",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1
"show_days": 1,
"show_seconds": 1
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-06-25 15:22:24.923835", "modified": "2021-06-03 18:01:51.097927",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee Onboarding", "name": "Employee Onboarding",

View File

@@ -11,14 +11,77 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet
from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer
class TestEmployeeOnboarding(unittest.TestCase): class TestEmployeeOnboarding(unittest.TestCase):
def test_employee_onboarding_incomplete_task(self): def setUp(self):
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
_set_up()
applicant = get_job_applicant()
job_offer = create_job_offer(job_applicant=applicant.name) project = "Employee Onboarding : Test Researcher - test@researcher.com"
frappe.db.sql("delete from tabProject where name=%s", project)
frappe.db.sql("delete from tabTask where project=%s", project)
def test_employee_onboarding_incomplete_task(self):
onboarding = create_employee_onboarding()
project_name = frappe.db.get_value('Project', onboarding.project, 'project_name')
self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com')
# don't allow making employee if onboarding is not complete
self.assertRaises(IncompleteTaskError, make_employee, onboarding.name)
# boarding status
self.assertEqual(onboarding.boarding_status, 'Pending')
# complete the task
project = frappe.get_doc('Project', onboarding.project)
for task in frappe.get_all('Task', dict(project=project.name)):
task = frappe.get_doc('Task', task.name)
task.status = 'Completed'
task.save()
# boarding status
onboarding.reload()
self.assertEqual(onboarding.boarding_status, 'Completed')
# make employee
onboarding.reload()
employee = make_employee(onboarding.name)
employee.first_name = employee.employee_name
employee.date_of_joining = nowdate()
employee.date_of_birth = '1990-05-08'
employee.gender = 'Female'
employee.insert()
self.assertEqual(employee.employee_name, 'Test Researcher')
def tearDown(self):
for entry in frappe.get_all('Employee Onboarding'):
doc = frappe.get_doc('Employee Onboarding', entry.name)
doc.cancel()
doc.delete()
def get_job_applicant():
if frappe.db.exists('Job Applicant', 'Test Researcher - test@researcher.com'):
return frappe.get_doc('Job Applicant', 'Test Researcher - test@researcher.com')
applicant = frappe.new_doc('Job Applicant')
applicant.applicant_name = 'Test Researcher'
applicant.email_id = 'test@researcher.com'
applicant.status = 'Open'
applicant.cover_letter = 'I am a great Researcher.'
applicant.insert()
return applicant
def get_job_offer(applicant_name):
job_offer = frappe.db.exists('Job Offer', {'job_applicant': applicant_name})
if job_offer:
return frappe.get_doc('Job Offer', job_offer)
job_offer = create_job_offer(job_applicant=applicant_name)
job_offer.submit() job_offer.submit()
return job_offer
def create_employee_onboarding():
applicant = get_job_applicant()
job_offer = get_job_offer(applicant.name)
onboarding = frappe.new_doc('Employee Onboarding') onboarding = frappe.new_doc('Employee Onboarding')
onboarding.job_applicant = applicant.name onboarding.job_applicant = applicant.name
@@ -38,44 +101,4 @@ class TestEmployeeOnboarding(unittest.TestCase):
onboarding.insert() onboarding.insert()
onboarding.submit() onboarding.submit()
project_name = frappe.db.get_value("Project", onboarding.project, "project_name") return onboarding
self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com')
# don't allow making employee if onboarding is not complete
self.assertRaises(IncompleteTaskError, make_employee, onboarding.name)
# complete the task
project = frappe.get_doc('Project', onboarding.project)
for task in frappe.get_all('Task', dict(project=project.name)):
task = frappe.get_doc('Task', task.name)
task.status = 'Completed'
task.save()
# make employee
onboarding.reload()
employee = make_employee(onboarding.name)
employee.first_name = employee.employee_name
employee.date_of_joining = nowdate()
employee.date_of_birth = '1990-05-08'
employee.gender = 'Female'
employee.insert()
self.assertEqual(employee.employee_name, 'Test Researcher')
def get_job_applicant():
if frappe.db.exists('Job Applicant', 'Test Researcher - test@researcher.com'):
return frappe.get_doc('Job Applicant', 'Test Researcher - test@researcher.com')
applicant = frappe.new_doc('Job Applicant')
applicant.applicant_name = 'Test Researcher'
applicant.email_id = 'test@researcher.com'
applicant.status = 'Open'
applicant.cover_letter = 'I am a great Researcher.'
applicant.insert()
return applicant
def _set_up():
for doctype in ["Employee Onboarding"]:
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
project = "Employee Onboarding : Test Researcher - test@researcher.com"
frappe.db.sql("delete from tabProject where name=%s", project)
frappe.db.sql("delete from tabTask where project=%s", project)

View File

@@ -23,20 +23,6 @@ frappe.ui.form.on('Employee Separation', {
frappe.set_route('List', 'Task', {project: frm.doc.project}); frappe.set_route('List', 'Task', {project: frm.doc.project});
},__("View")); },__("View"));
} }
if (frm.doc.docstatus === 1 && frm.doc.project) {
frappe.call({
method: "erpnext.hr.utils.get_boarding_status",
args: {
"project": frm.doc.project
},
callback: function(r) {
if (r.message) {
frm.set_value('boarding_status', r.message);
}
refresh_field("boarding_status");
}
});
}
}, },
employee_separation_template: function(frm) { employee_separation_template: function(frm) {

View File

@@ -50,11 +50,12 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"default": "Pending",
"fieldname": "boarding_status", "fieldname": "boarding_status",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"options": "\nPending\nIn Process\nCompleted", "options": "Pending\nIn Process\nCompleted",
"reqd": 1 "read_only": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@@ -147,7 +148,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-04-28 15:58:36.020196", "modified": "2021-06-03 18:02:54.007313",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee Separation", "name": "Employee Separation",

View File

@@ -6,11 +6,35 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
test_dependencies = ["Employee Onboarding"] test_dependencies = ['Employee Onboarding']
class TestEmployeeSeparation(unittest.TestCase): class TestEmployeeSeparation(unittest.TestCase):
def test_employee_separation(self): def test_employee_separation(self):
employee = frappe.db.get_value("Employee", {"status": "Active"}) separation = create_employee_separation()
self.assertEqual(separation.docstatus, 1)
self.assertEqual(separation.boarding_status, 'Pending')
project = frappe.get_doc('Project', separation.project)
project.percent_complete_method = 'Manual'
project.status = 'Completed'
project.save()
separation.reload()
self.assertEqual(separation.boarding_status, 'Completed')
separation.cancel()
self.assertEqual(separation.project, '')
def tearDown(self):
for entry in frappe.get_all('Employee Separation'):
doc = frappe.get_doc('Employee Separation', entry.name)
if doc.docstatus == 1:
doc.cancel()
doc.delete()
def create_employee_separation():
employee = frappe.db.get_value('Employee', {'status': 'Active'})
separation = frappe.new_doc('Employee Separation') separation = frappe.new_doc('Employee Separation')
separation.employee = employee separation.employee = employee
separation.company = '_Test Company' separation.company = '_Test Company'
@@ -21,6 +45,4 @@ class TestEmployeeSeparation(unittest.TestCase):
separation.boarding_status = 'Pending' separation.boarding_status = 'Pending'
separation.insert() separation.insert()
separation.submit() separation.submit()
self.assertEqual(separation.docstatus, 1) return separation
separation.cancel()
self.assertEqual(separation.project, "")

View File

@@ -114,16 +114,23 @@ def get_onboarding_details(parent, parenttype):
filters={"parent": parent, "parenttype": parenttype}, filters={"parent": parent, "parenttype": parenttype},
order_by= "idx") order_by= "idx")
@frappe.whitelist() def update_employee_boarding_status(project):
def get_boarding_status(project): employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name})
employee_separation = frappe.db.exists('Employee Separation', {'project': project.name})
if not (employee_onboarding or employee_separation):
return
status = 'Pending' status = 'Pending'
if project: if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0:
doc = frappe.get_doc('Project', project)
if flt(doc.percent_complete) > 0.0 and flt(doc.percent_complete) < 100.0:
status = 'In Process' status = 'In Process'
elif flt(doc.percent_complete) == 100.0: elif flt(project.percent_complete) == 100.0:
status = 'Completed' status = 'Completed'
return status
if employee_onboarding:
frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status)
elif employee_separation:
frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status)
def set_employee_name(doc): def set_employee_name(doc):
if doc.employee and not doc.employee_name: if doc.employee and not doc.employee_name:

View File

@@ -14,6 +14,7 @@ from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_e
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list
from erpnext.hr.utils import update_employee_boarding_status
class Project(Document): class Project(Document):
def get_feed(self): def get_feed(self):
@@ -37,6 +38,7 @@ class Project(Document):
self.send_welcome_email() self.send_welcome_email()
self.update_costing() self.update_costing()
self.update_percent_complete() self.update_percent_complete()
update_employee_boarding_status(self)
def copy_from_template(self): def copy_from_template(self):
''' '''
@@ -132,6 +134,7 @@ class Project(Document):
def update_project(self): def update_project(self):
'''Called externally by Task''' '''Called externally by Task'''
self.update_percent_complete() self.update_percent_complete()
update_employee_boarding_status(self)
self.update_costing() self.update_costing()
self.db_update() self.db_update()