Staffing Plan (#14346)

* validate staffing plan

* staffing plan docs

* get_active_staffing_plan_and_vacancies now returns dict, mandatory fields set

* validate with parent and child company plans, plan now considers open job openings

* Failure details in validate error messages

* Fixed sql injection
diff --git a/erpnext/docs/assets/img/human-resources/staffing-plan-detail.png b/erpnext/docs/assets/img/human-resources/staffing-plan-detail.png
new file mode 100644
index 0000000..63532b7
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/staffing-plan-detail.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/staffing-plan.png b/erpnext/docs/assets/img/human-resources/staffing-plan.png
new file mode 100644
index 0000000..f655029
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/staffing-plan.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/human-resources/staffing-plan.md b/erpnext/docs/user/manual/en/human-resources/staffing-plan.md
new file mode 100644
index 0000000..d16a1ec
--- /dev/null
+++ b/erpnext/docs/user/manual/en/human-resources/staffing-plan.md
@@ -0,0 +1,18 @@
+# Staffing Plan
+Staffing Plan helps you to plan human resource recruitments for your Company. ERPNext allows you to do this at a group company level helping you efficiently plan and budget new hirings for a period. Job Openings can only be created as per the number of vacancies and budget as per the active Staffing Plan.
+
+> Human Resources > Setup > Staffing Plan > New Staffing Plan
+
+<img class="screenshot" alt="Staffing Plan"
+	src="{{docs_base_url}}/assets/img/human-resources/staffing-plan.png">
+
+- **Designation:** The designations for which you are creating the Staffing Plan.
+- **Number of Positions:** The number of positions you plan to recruit for between the Staffing Plan from and to dates.
+- **Current Count:** This is the number of Employees already hired for the Designation.
+- **Vacancies:** The number of vacancies based on the Number of Positions you wish to recruit and the current Employee count.
+- **Estimated Cost Per Position:** You can specify the cost to company per position so that hiring officials can stick to the budget.
+
+<img class="screenshot" alt="Staffing Plan Detail"
+	src="{{docs_base_url}}/assets/img/human-resources/staffing-plan-detail.png">
+
+**Total Estimated Budget** Once you enter the recruitment plan for all the designations, Staffing Plan will draw up the total estimated budget.
diff --git a/erpnext/hr/doctype/job_opening/job_opening.js b/erpnext/hr/doctype/job_opening/job_opening.js
index 7b0e447..b303b24 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.js
+++ b/erpnext/hr/doctype/job_opening/job_opening.js
@@ -14,7 +14,7 @@
 	designation: function(frm) {
 		if(frm.doc.designation && frm.doc.company){
 			frappe.call({
-				"method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_active_staffing_plan_and_vacancies",
+				"method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_active_staffing_plan_details",
 				args: {
 					company: frm.doc.company,
 					designation: frm.doc.designation,
@@ -23,8 +23,8 @@
 				},
 				callback: function (data) {
 					if(data.message){
-						frm.set_value('staffing_plan', data.message[0]);
-						frm.set_value('planned_vacancies', data.message[1]);
+						frm.set_value('staffing_plan', data.message[0].name);
+						frm.set_value('planned_vacancies', data.message[0].vacancies);
 					} else {
 						frm.set_value('staffing_plan', "");
 						frm.set_value('planned_vacancies', 0);
diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py
index b579d6f..cfe6290 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -8,7 +8,7 @@
 
 from frappe.website.website_generator import WebsiteGenerator
 from frappe import _
-from erpnext.hr.doctype.staffing_plan.staffing_plan import get_current_employee_count, get_active_staffing_plan_and_vacancies
+from erpnext.hr.doctype.staffing_plan.staffing_plan import get_designation_counts, get_active_staffing_plan_details
 
 class JobOpening(WebsiteGenerator):
 	website = frappe._dict(
@@ -24,11 +24,11 @@
 
 	def validate_current_vacancies(self):
 		if not self.staffing_plan:
-			vacancies = get_active_staffing_plan_and_vacancies(self.company,
+			staffing_plan = get_active_staffing_plan_details(self.company,
 				self.designation, self.department)
-			if vacancies:
-				self.staffing_plan = vacancies[0]
-				self.planned_vacancies = vacancies[1]
+			if staffing_plan:
+				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`
@@ -39,14 +39,13 @@
 			staffing_plan_company = frappe.db.get_value("Staffing Plan", self.staffing_plan, "company")
 			lft, rgt = frappe.db.get_value("Company", staffing_plan_company, ["lft", "rgt"])
 
-			current_count = get_current_employee_count(self.designation, staffing_plan_company)
-			current_count+= frappe.db.sql("""select count(*) from `tabJob Opening` \
-				where designation=%s and status='Open'
-					and company in (select name from tabCompany where lft>=%s and rgt<=%s)
-				""", (self.designation, lft, rgt))[0][0]
+			designation_counts = get_designation_counts(self.designation, self.company)
+			current_count = designation_counts['employee_count'] + designation_counts['job_openings']
 
 			if self.planned_vacancies <= current_count:
-				frappe.throw(_("Job Openings for designation {0} and company {1} already opened or hiring completed as per Staffing Plan {2}".format(self.designation, staffing_plan_company, self.staffing_plan)))
+				frappe.throw(_("Job Openings for designation {0} already open \
+					or hiring completed as per Staffing Plan {1}"
+					.format(self.designation, self.staffing_plan)))
 
 	def get_context(self, context):
 		context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.js b/erpnext/hr/doctype/staffing_plan/staffing_plan.js
index ca57d9f..4fbc6b3 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.js
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.js
@@ -33,17 +33,22 @@
 		let child = locals[cdt][cdn]
 		if(frm.doc.company && child.designation){
 			frappe.call({
-				"method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_current_employee_count",
+				"method": "erpnext.hr.doctype.staffing_plan.staffing_plan.get_designation_counts",
 				args: {
 					designation: child.designation,
 					company: frm.doc.company
 				},
 				callback: function (data) {
 					if(data.message){
-						frappe.model.set_value(cdt, cdn, 'current_count', data.message);
+						frappe.model.set_value(cdt, cdn, 'current_count', data.message.employee_count);
+						frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings);
+						if (child.number_of_positions < (data.message.employee_count +  data.message.job_openings)){
+							frappe.model.set_value(cdt, cdn, 'number_of_positions', data.message.employee_count +  data.message.job_openings);
+						}
 					}
 					else{ // No employees for this designation
 						frappe.model.set_value(cdt, cdn, 'current_count', 0);
+						frappe.model.set_value(cdt, cdn, 'current_openings', 0);
 					}
 				}
 			});
@@ -67,19 +72,25 @@
 
 var set_vacancies = function(frm, cdt, cdn) {
 	let child = locals[cdt][cdn]
-	if(child.number_of_positions) {
-		frappe.model.set_value(cdt, cdn, 'vacancies', child.number_of_positions - child.current_count);
+	if (child.number_of_positions < (child.current_count + child.current_openings)){
+		frappe.throw(__("Number of positions cannot be less then current count of employees"))
+	}
+
+	if(child.number_of_positions > 0) {
+		frappe.model.set_value(cdt, cdn, 'vacancies', child.number_of_positions - (child.current_count + child.current_openings));
 	}
 	else{
 		frappe.model.set_value(cdt, cdn, 'vacancies', 0);
 	}
+
 	set_total_estimated_cost(frm, cdt, cdn);
 }
 
 // Note: Estimated Cost is calculated on number of Vacancies
+// Validate for > 0 ?
 var set_total_estimated_cost = function(frm, cdt, cdn) {
 	let child = locals[cdt][cdn]
-	if(child.number_of_positions && child.estimated_cost_per_position) {
+	if(child.vacancies > 0 && child.estimated_cost_per_position) {
 		frappe.model.set_value(cdt, cdn, 'total_estimated_cost', child.vacancies * child.estimated_cost_per_position);
 	}
 	else {
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.json b/erpnext/hr/doctype/staffing_plan/staffing_plan.json
index 229cc05..9576bc3 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.json
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.json
@@ -15,6 +15,7 @@
  "fields": [
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -47,6 +48,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -79,6 +81,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -109,6 +112,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -132,7 +136,7 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
@@ -140,6 +144,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -163,7 +168,7 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
@@ -171,6 +176,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -202,6 +208,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -234,6 +241,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -264,6 +272,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -296,6 +305,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -336,7 +346,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-18 19:10:34.394249", 
+ "modified": "2018-05-28 18:30:27.041395", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Staffing Plan", 
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 37ff5cb..e1a5f8c 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -6,7 +6,7 @@
 import frappe
 from frappe.model.document import Document
 from frappe import _
-from frappe.utils import getdate, nowdate
+from frappe.utils import getdate, nowdate, cint, flt
 
 class StaffingPlan(Document):
 	def validate(self):
@@ -14,32 +14,128 @@
 		if self.from_date and self.to_date and self.from_date > self.to_date:
 			frappe.throw(_("From Date cannot be greater than To Date"))
 
-		# Validate if any submitted Staffing Plan exist for Designations in this plan
-		# and spd.vacancies>0 ?
-		for detail in self.get("staffing_details"):
-			overlap = (frappe.db.sql("""select spd.parent \
-				from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name \
-				where spd.designation='{0}' and sp.docstatus=1 \
-				and sp.to_date >= '{1}' and sp.from_date <='{2}'"""
-			.format(detail.designation, self.from_date, self.to_date)))
+		self.total_estimated_budget = 0
 
-			if overlap and overlap [0][0]:
-				frappe.throw(_("Staffing Plan {0} already exist for designation {1}"
-					.format(overlap[0][0], detail.designation)))
+		for detail in self.get("staffing_details"):
+			self.validate_overlap(detail)
+			self.validate_with_subsidiary_plans(detail)
+			self.validate_with_parent_plan(detail)
+
+			#Set readonly fields
+			designation_counts = get_designation_counts(detail.designation, self.company)
+			detail.current_count = designation_counts['employee_count']
+			detail.current_openings = designation_counts['job_openings']
+
+			if detail.number_of_positions < (detail.current_count + detail.current_openings):
+				frappe.throw(_("Number of positions cannot be less then current count of employees"))
+			elif detail.number_of_positions > 0:
+				detail.vacancies = detail.number_of_positions - (detail.current_count + detail.current_openings)
+				if detail.vacancies > 0 and detail.estimated_cost_per_position:
+					detail.total_estimated_cost = detail.vacancies * detail.estimated_cost_per_position
+				else: detail.total_estimated_cost = 0
+			else: detail.vacancies = detail.number_of_positions = detail.total_estimated_cost = 0
+			self.total_estimated_budget += detail.total_estimated_cost
+
+	def validate_overlap(self, staffing_plan_detail):
+		# Validate if any submitted Staffing Plan exist for any Designations in this plan
+		# and spd.vacancies>0 ?
+		overlap = frappe.db.sql("""select spd.parent
+			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
+			where spd.designation=%s and sp.docstatus=1
+			and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s
+		""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company))
+		if overlap and overlap [0][0]:
+			frappe.throw(_("Staffing Plan {0} already exist for designation {1}"
+				.format(overlap[0][0], staffing_plan_detail.designation)))
+
+	def validate_with_parent_plan(self, staffing_plan_detail):
+		if not frappe.db.get_value("Company", self.company, "parent_company"):
+			return # No parent, nothing to validate
+
+		# Get staffing plan applicable for the company (Parent Company)
+		parent_plan_details = get_active_staffing_plan_details(self.company, staffing_plan_detail.designation)
+		if not parent_plan_details:
+			return #no staffing plan for any parent Company in herarchy
+
+		# Fetch parent company which owns the staffing plan. NOTE: Parent could be higher up in the heirarchy
+		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):
+			frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
+				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)))
+
+		#Get vacanices already planned for all companies down the herarchy 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
+			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
+			where spd.designation=%s and sp.docstatus=1
+			and sp.to_date >= %s and sp.from_date <=%s
+			and sp.company in (select name from tabCompany where lft > %s and rgt < %s)
+		""", (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 \
+			(flt(parent_plan_details[0].total_estimated_cost) < \
+			(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}"
+				.format(cint(all_sibling_details.vacancies),
+					all_sibling_details.total_estimated_cost,
+					frappe.bold(staffing_plan_detail.designation),
+					parent_company,
+					cint(parent_plan_details[0].vacancies),
+					parent_plan_details[0].total_estimated_cost,
+					parent_plan_details[0].name)))
+
+	def validate_with_subsidiary_plans(self, staffing_plan_detail):
+		#Valdate this plan with all child company plan
+		children_details = frappe.db.sql("""select sum(spd.vacancies) as vacancies,
+			sum(spd.total_estimated_cost) as total_estimated_cost
+			from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
+			where spd.designation=%s and sp.docstatus=1
+			and sp.to_date >= %s and sp.from_date <=%s
+			and sp.company in (select name from tabCompany where parent_company = %s)
+		""", (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):
+			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.whitelist()
-def get_current_employee_count(designation, company):
+def get_designation_counts(designation, company):
 	if not designation:
 		return False
 
+	employee_counts_dict = {}
 	lft, rgt = frappe.db.get_value("Company", company, ["lft", "rgt"])
-	employee_count = frappe.db.sql("""select count(*) from `tabEmployee`
+	employee_counts_dict["employee_count"] = frappe.db.sql("""select count(*) from `tabEmployee`
 		where designation = %s and status='Active'
 			and company in (select name from tabCompany where lft>=%s and rgt<=%s)
 		""", (designation, lft, rgt))[0][0]
-	return employee_count
 
-def get_active_staffing_plan_and_vacancies(company, designation, department=None, date=getdate(nowdate())):
+	employee_counts_dict['job_openings'] = frappe.db.sql("""select count(*) from `tabJob Opening` \
+		where designation=%s and status='Open'
+			and company in (select name from tabCompany where lft>=%s and rgt<=%s)
+		""", (designation, lft, rgt))[0][0]
+
+	return employee_counts_dict
+
+@frappe.whitelist()
+def get_active_staffing_plan_details(company, designation, department=None, date=getdate(nowdate())):
 	if not company or not designation:
 		frappe.throw(_("Please select Company and Designation"))
 
@@ -51,16 +147,16 @@
 		conditions += " and '{0}' between sp.from_date and sp.to_date".format(date)
 
 	staffing_plan = frappe.db.sql("""
-		select sp.name, spd.vacancies
+		select sp.name, spd.vacancies, spd.total_estimated_cost
 		from `tabStaffing Plan Detail` spd join `tabStaffing Plan` sp on spd.parent=sp.name
 		where company=%s and spd.designation=%s and sp.docstatus=1 {0}
-	""".format(conditions), (company, designation))
+	""".format(conditions), (company, designation), as_dict = 1)
 
 	if not staffing_plan:
 		parent_company = frappe.db.get_value("Company", company, "parent_company")
 		if parent_company:
-			staffing_plan = get_active_staffing_plan_and_vacancies(parent_company,
+			staffing_plan = get_active_staffing_plan_details(parent_company,
 				designation, department, date)
 
-	# Only a signle staffing plan can be active for a designation on given date
-	return staffing_plan[0] if staffing_plan else None
+	# Only a single staffing plan can be active for a designation on given date
+	return staffing_plan if staffing_plan else None
diff --git a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
index eb77b43..f1d1609 100644
--- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
+++ b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json
@@ -1,258 +1,297 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-04-13 18:04:20.978931", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-04-13 18:04:20.978931",
+ "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": "designation", 
-   "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": "Designation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Designation", 
-   "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, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "designation",
+   "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": "Designation",
+   "length": 0,
+   "no_copy": 0,
+   "options": "Designation",
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 0,
+   "remember_last_selected_value": 0,
+   "report_hide": 0,
+   "reqd": 1,
+   "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": "number_of_positions", 
-   "fieldtype": "Int", 
-   "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": "Number Of Positions", 
-   "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, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "number_of_positions",
+   "fieldtype": "Int",
+   "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": "Number Of Positions",
+   "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": "estimated_cost_per_position", 
-   "fieldtype": "Currency", 
-   "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": "Estimated Cost Per Position", 
-   "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, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "estimated_cost_per_position",
+   "fieldtype": "Currency",
+   "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": "Estimated Cost Per Position",
+   "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": "total_estimated_cost", 
-   "fieldtype": "Currency", 
-   "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": "Total Estimated Cost", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "column_break_5",
+   "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, 
-   "fieldname": "column_break_5", 
-   "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, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "current_count",
+   "fieldtype": "Int",
+   "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": "Current Count",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 1,
+   "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": "current_count", 
-   "fieldtype": "Int", 
-   "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": "Current Count", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "current_openings",
+   "fieldtype": "Int",
+   "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": "Current Openings",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 1,
+   "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": "vacancies", 
-   "fieldtype": "Int", 
-   "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": "Vacancies", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
+   "allow_bulk_edit": 0,
+   "allow_in_quick_entry": 0,
+   "allow_on_submit": 0,
+   "bold": 0,
+   "collapsible": 0,
+   "columns": 0,
+   "fieldname": "vacancies",
+   "fieldtype": "Int",
+   "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": "Vacancies",
+   "length": 0,
+   "no_copy": 0,
+   "permlevel": 0,
+   "precision": "",
+   "print_hide": 0,
+   "print_hide_if_no_value": 0,
+   "read_only": 1,
+   "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-04-15 16:09:12.622186", 
- "modified_by": "Administrator", 
- "module": "HR", 
- "name": "Staffing Plan Detail", 
- "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, 
+ },
+ {
+	"allow_bulk_edit": 0,
+	"allow_in_quick_entry": 0,
+	"allow_on_submit": 0,
+	"bold": 0,
+	"collapsible": 0,
+	"columns": 0,
+	"fieldname": "total_estimated_cost",
+	"fieldtype": "Currency",
+	"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": "Total Estimated Cost",
+	"length": 0,
+	"no_copy": 0,
+	"permlevel": 0,
+	"precision": "",
+	"print_hide": 0,
+	"print_hide_if_no_value": 0,
+	"read_only": 1,
+	"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-06-01 17:03:38.020993",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Staffing Plan Detail",
+ "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
-}
\ No newline at end of file
+}