diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py index c71407d71d..ce7caa33c6 100644 --- a/erpnext/hr/doctype/job_opening/job_opening.py +++ b/erpnext/hr/doctype/job_opening/job_opening.py @@ -6,6 +6,7 @@ import frappe from frappe import _ +from frappe.utils import get_link_to_form from frappe.website.website_generator import WebsiteGenerator from erpnext.hr.doctype.staffing_plan.staffing_plan import ( @@ -33,26 +34,32 @@ class JobOpening(WebsiteGenerator): self.staffing_plan = staffing_plan[0].name self.planned_vacancies = staffing_plan[0].vacancies elif not self.planned_vacancies: - planned_vacancies = frappe.db.sql( - """ - select vacancies from `tabStaffing Plan Detail` - where parent=%s and designation=%s""", - (self.staffing_plan, self.designation), + self.planned_vacancies = frappe.db.get_value( + "Staffing Plan Detail", + {"parent": self.staffing_plan, "designation": self.designation}, + "vacancies", ) - self.planned_vacancies = planned_vacancies[0][0] if planned_vacancies else None if self.staffing_plan and self.planned_vacancies: staffing_plan_company = frappe.db.get_value("Staffing Plan", self.staffing_plan, "company") - lft, rgt = frappe.get_cached_value("Company", staffing_plan_company, ["lft", "rgt"]) - designation_counts = get_designation_counts(self.designation, self.company) + designation_counts = get_designation_counts(self.designation, self.company, self.name) current_count = designation_counts["employee_count"] + designation_counts["job_openings"] - if self.planned_vacancies <= current_count: + number_of_positions = frappe.db.get_value( + "Staffing Plan Detail", + {"parent": self.staffing_plan, "designation": self.designation}, + "number_of_positions", + ) + + if number_of_positions <= current_count: frappe.throw( _( - "Job Openings for designation {0} already open or hiring completed as per Staffing Plan {1}" - ).format(self.designation, self.staffing_plan) + "Job Openings for the designation {0} are already open or the hiring is complete as per the Staffing Plan {1}" + ).format( + frappe.bold(self.designation), get_link_to_form("Staffing Plan", self.staffing_plan) + ), + title=_("Vacancies fulfilled"), ) def get_context(self, context): diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.py b/erpnext/hr/doctype/job_opening/test_job_opening.py index a72a6eb338..e991054f62 100644 --- a/erpnext/hr/doctype/job_opening/test_job_opening.py +++ b/erpnext/hr/doctype/job_opening/test_job_opening.py @@ -3,8 +3,77 @@ import unittest -# test_records = frappe.get_test_records('Job Opening') +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days, getdate + +from erpnext.hr.doctype.employee.test_employee import make_employee +from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company -class TestJobOpening(unittest.TestCase): - pass +class TestJobOpening(FrappeTestCase): + def setUp(self): + frappe.db.delete("Staffing Plan") + frappe.db.delete("Staffing Plan Detail") + frappe.db.delete("Job Opening") + + make_company("_Test Opening Company", "_TOC") + frappe.db.delete("Employee", {"company": "_Test Opening Company"}) + + def test_vacancies_fulfilled(self): + make_employee( + "test_job_opening@example.com", company="_Test Opening Company", designation="Designer" + ) + + staffing_plan = frappe.get_doc( + { + "doctype": "Staffing Plan", + "company": "_Test Opening Company", + "name": "Test", + "from_date": getdate(), + "to_date": add_days(getdate(), 10), + } + ) + + staffing_plan.append( + "staffing_details", + {"designation": "Designer", "vacancies": 1, "estimated_cost_per_position": 50000}, + ) + staffing_plan.insert() + staffing_plan.submit() + + self.assertEqual(staffing_plan.staffing_details[0].number_of_positions, 2) + + # allows creating 1 job opening as per vacancy + opening_1 = get_job_opening() + opening_1.insert() + + # vacancies as per staffing plan already fulfilled via job opening and existing employee count + opening_2 = get_job_opening(job_title="Designer New") + self.assertRaises(frappe.ValidationError, opening_2.insert) + + # allows updating existing job opening + opening_1.status = "Closed" + opening_1.save() + + +def get_job_opening(**args): + args = frappe._dict(args) + + opening = frappe.db.exists("Job Opening", {"job_title": args.job_title or "Designer"}) + if opening: + return frappe.get_doc("Job Opening", opening) + + opening = frappe.get_doc( + { + "doctype": "Job Opening", + "job_title": "Designer", + "designation": "Designer", + "company": "_Test Opening Company", + "status": "Open", + } + ) + + opening.update(args) + + return opening diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py index ce7e50f7f4..82472dec41 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py @@ -172,27 +172,24 @@ class StaffingPlan(Document): @frappe.whitelist() -def get_designation_counts(designation, company): +def get_designation_counts(designation, company, job_opening=None): if not designation: return False - employee_counts = {} company_set = get_descendants_of("Company", company) company_set.append(company) - employee_counts["employee_count"] = frappe.db.get_value( - "Employee", - filters={"designation": designation, "status": "Active", "company": ("in", company_set)}, - fieldname=["count(name)"], + employee_count = frappe.db.count( + "Employee", {"designation": designation, "status": "Active", "company": ("in", company_set)} ) - employee_counts["job_openings"] = frappe.db.get_value( - "Job Opening", - filters={"designation": designation, "status": "Open", "company": ("in", company_set)}, - fieldname=["count(name)"], - ) + filters = {"designation": designation, "status": "Open", "company": ("in", company_set)} + if job_opening: + filters["name"] = ("!=", job_opening) - return employee_counts + job_openings = frappe.db.count("Job Opening", filters) + + return {"employee_count": employee_count, "job_openings": job_openings} @frappe.whitelist() diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py index a3adbbd56a..ac69c21979 100644 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py @@ -85,13 +85,16 @@ def _set_up(): make_company() -def make_company(): - if frappe.db.exists("Company", "_Test Company 10"): +def make_company(name=None, abbr=None): + if not name: + name = "_Test Company 10" + + if frappe.db.exists("Company", name): return company = frappe.new_doc("Company") - company.company_name = "_Test Company 10" - company.abbr = "_TC10" + company.company_name = name + company.abbr = abbr or "_TC10" company.parent_company = "_Test Company 3" company.default_currency = "INR" company.country = "Pakistan"