[Tests] Staffing Plan (#14585)

* Add test case for Staffing Plan

* Fix codacy
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 9632956..ac3f25f 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -8,6 +8,9 @@
 from frappe import _
 from frappe.utils import getdate, nowdate, cint, flt
 
+class SubsidiaryCompanyError(frappe.ValidationError): pass
+class ParentCompanyError(frappe.ValidationError): pass
+
 class StaffingPlan(Document):
 	def validate(self):
 		# Validate Dates
@@ -55,23 +58,22 @@
 		# Get staffing plan applicable for the company (Parent Company)
 		parent_plan_details = get_active_staffing_plan_details(self.company, staffing_plan_detail.designation, self.from_date, self.to_date)
 		if not parent_plan_details:
-			return #no staffing plan for any parent Company in herarchy
+			return #no staffing plan for any parent Company in hierarchy
 
-		# Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the heirarchy
+		# Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the hierarchy
 		parent_company = frappe.db.get_value("Staffing Plan", parent_plan_details[0].name, "company")
-
 		# Parent plan available, validate with parent, siblings as well as children of staffing plan Company
-		if staffing_plan_detail.vacancies > cint(parent_plan_details[0].vacancies) or \
-			staffing_plan_detail.total_estimated_cost > flt(parent_plan_details[0].total_estimated_cost):
+		if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or \
+			flt(staffing_plan_detail.total_estimated_cost) > flt(parent_plan_details[0].total_estimated_cost):
 			frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
-				for {2} as per staffing plan {3} for parent company {4}"
+				for {2} as per staffing plan {3} for parent company {4}."
 				.format(cint(parent_plan_details[0].vacancies),
 					parent_plan_details[0].total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
 					parent_plan_details[0].name,
-					parent_company)))
+					parent_company)), ParentCompanyError)
 
-		#Get vacanices already planned for all companies down the herarchy of Parent Company
+		#Get vacanices already planned for all companies down the hierarchy of Parent Company
 		lft, rgt = frappe.db.get_value("Company", parent_company, ["lft", "rgt"])
 		all_sibling_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
 			sum(spd.total_estimated_cost) as total_estimated_cost
@@ -82,11 +84,11 @@
 		""", (staffing_plan_detail.designation, self.from_date, self.to_date, lft, rgt), as_dict = 1)[0]
 
 		if (cint(parent_plan_details[0].vacancies) < \
-			(staffing_plan_detail.vacancies + cint(all_sibling_details.vacancies))) or \
+			(cint(staffing_plan_detail.vacancies) + cint(all_sibling_details.vacancies))) or \
 			(flt(parent_plan_details[0].total_estimated_cost) < \
-			(staffing_plan_detail.total_estimated_cost + flt(all_sibling_details.total_estimated_cost))):
+			(flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost))):
 			frappe.throw(_("{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
-				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}"
+				You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
 				.format(cint(all_sibling_details.vacancies),
 					all_sibling_details.total_estimated_cost,
 					frappe.bold(staffing_plan_detail.designation),
@@ -106,14 +108,14 @@
 		""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company), as_dict = 1)[0]
 
 		if children_details and \
-			staffing_plan_detail.vacancies < cint(children_details.vacancies) or \
-			staffing_plan_detail.total_estimated_cost < flt(children_details.total_estimated_cost):
+			cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) or \
+			flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost):
 			frappe.throw(_("Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
 				Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
 				.format(self.company,
 					cint(children_details.vacancies),
 					children_details.total_estimated_cost,
-					frappe.bold(staffing_plan_detail.designation))))
+					frappe.bold(staffing_plan_detail.designation))), SubsidiaryCompanyError)
 
 @frappe.whitelist()
 def get_designation_counts(designation, company):
diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
index 426ab3a..d95284c 100644
--- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py
@@ -5,6 +5,98 @@
 
 import frappe
 import unittest
+from erpnext.hr.doctype.staffing_plan.staffing_plan import SubsidiaryCompanyError
+from erpnext.hr.doctype.staffing_plan.staffing_plan import ParentCompanyError
+from frappe.utils import nowdate, add_days
+
+test_dependencies = ["Designation"]
 
 class TestStaffingPlan(unittest.TestCase):
-	pass
+	def test_staffing_plan(self):
+		_set_up()
+		frappe.db.set_value("Company", "_Test Company", "is_group", 1)
+		make_company()
+		set_employees()
+		if frappe.db.exists("Staffing Plan", "Test"):
+			return
+		staffing_plan = frappe.new_doc("Staffing Plan")
+		staffing_plan.company = "_Test Company 3"
+		staffing_plan.name = "Test"
+		staffing_plan.from_date = nowdate()
+		staffing_plan.to_date = add_days(nowdate(), 10)
+		staffing_plan.append("staffing_details", {
+			"designation": "Researcher",
+			"number_of_positions": 6,
+			"estimated_cost_per_position": 50000
+		})
+		staffing_plan.insert()
+		staffing_plan.submit()
+		self.assertEqual(staffing_plan.total_estimated_budget, 250000.00)
+
+	def test_staffing_plan_subsidiary_company(self):
+		if frappe.db.exists("Staffing Plan", "Test 1"):
+			return
+		staffing_plan = frappe.new_doc("Staffing Plan")
+		staffing_plan.company = "_Test Company"
+		staffing_plan.name = "Test 1"
+		staffing_plan.from_date = nowdate()
+		staffing_plan.to_date = add_days(nowdate(), 10)
+		staffing_plan.append("staffing_details", {
+			"designation": "Researcher",
+			"number_of_positions": 3,
+			"estimated_cost_per_position": 45000
+		})
+		self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert)
+
+	def test_staffing_plan_parent_company(self):
+		_set_up()
+		if frappe.db.exists("Staffing Plan", "Test"):
+			return
+		staffing_plan = frappe.new_doc("Staffing Plan")
+		staffing_plan.company = "_Test Company"
+		staffing_plan.name = "Test"
+		staffing_plan.from_date = nowdate()
+		staffing_plan.to_date = add_days(nowdate(), 10)
+		staffing_plan.append("staffing_details", {
+			"designation": "Researcher",
+			"number_of_positions": 7,
+			"estimated_cost_per_position": 50000
+		})
+		staffing_plan.insert()
+		staffing_plan.submit()
+		self.assertEqual(staffing_plan.total_estimated_budget, 250000.00)
+		if frappe.db.exists("Staffing Plan", "Test 1"):
+			return
+		staffing_plan = frappe.new_doc("Staffing Plan")
+		staffing_plan.company = "_Test Company 3"
+		staffing_plan.name = "Test 1"
+		staffing_plan.from_date = nowdate()
+		staffing_plan.to_date = add_days(nowdate(), 10)
+		staffing_plan.append("staffing_details", {
+			"designation": "Researcher",
+			"number_of_positions": 7,
+			"estimated_cost_per_position": 60000
+		})
+		staffing_plan.insert()
+		self.assertRaises(ParentCompanyError, staffing_plan.submit)
+
+def _set_up():
+	for doctype in ["Staffing Plan", "Staffing Plan Detail"]:
+		frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
+
+def make_company():
+	if frappe.db.exists("Company", "_Test Company 3"):
+		return
+	company = frappe.new_doc("Company")
+	company.company_name = "_Test Company 3"
+	company.abbr = "_TC3"
+	company.parent_company = "_Test Company"
+	company.default_currency = "INR"
+	company.country = "India"
+	company.insert()
+
+def set_employees():
+	frappe.db.set_value("Employee", "_T-Employee-00001", "designation", "Researcher")
+	frappe.db.set_value("Employee", "_T-Employee-00001", "company", "_Test Company")
+	frappe.db.set_value("Employee", "_T-Employee-00002", "designation", "Researcher")
+	frappe.db.set_value("Employee", "_T-Employee-00002", "company", "_Test Company 3")