diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py index 3b00bd327a..3b300941a6 100644 --- a/erpnext/hr/doctype/designation/test_designation.py +++ b/erpnext/hr/doctype/designation/test_designation.py @@ -4,4 +4,17 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Designation') \ No newline at end of file +# test_records = frappe.get_test_records('Designation') + +def create_designation(**args): + args = frappe._dict(args) + if frappe.db.exists("Designation", args.designation_name or "_Test designation"): + return frappe.get_doc("Designation", args.designation_name or "_Test designation") + + designation = frappe.get_doc({ + "doctype": "Designation", + "designation_name": args.designation_name or "_Test designation", + "description": args.description or "_Test description" + }) + designation.save() + return designation \ No newline at end of file diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index af87f85743..cf418b0e8f 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -12,6 +12,7 @@ from frappe.permissions import add_user_permission, remove_user_permission, \ from frappe.model.document import Document from erpnext.utilities.transaction_base import delete_events from frappe.utils.nestedset import NestedSet +from erpnext.hr.doctype.job_offer.job_offer import get_staffing_plan_detail class EmployeeUserDisabledError(frappe.ValidationError): pass class EmployeeLeftValidationError(frappe.ValidationError): pass diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 3f5a2ab333..8dd0acf455 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -3,6 +3,7 @@ "doctype": "DocType", "document_type": "Other", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "employee_settings", "retirement_age", @@ -22,7 +23,9 @@ "leave_status_notification_template", "column_break_18", "leave_approver_mandatory_in_leave_application", - "show_leaves_of_all_department_members_in_calendar" + "show_leaves_of_all_department_members_in_calendar", + "hiring_settings", + "check_vacancies" ], "fields": [ { @@ -44,18 +47,6 @@ "label": "Employee Records to be created by", "options": "Naming Series\nEmployee Number\nFull Name" }, - { - "fieldname": "leave_approval_notification_template", - "fieldtype": "Link", - "label": "Leave Approval Notification Template", - "options": "Email Template" - }, - { - "fieldname": "leave_status_notification_template", - "fieldtype": "Link", - "label": "Leave Status Notification Template", - "options": "Email Template" - }, { "fieldname": "column_break_4", "fieldtype": "Column Break" @@ -67,12 +58,6 @@ "fieldtype": "Check", "label": "Stop Birthday Reminders" }, - { - "default": "1", - "fieldname": "leave_approver_mandatory_in_leave_application", - "fieldtype": "Check", - "label": "Leave Approver Mandatory In Leave Application" - }, { "default": "1", "fieldname": "expense_approver_mandatory_in_expense_claim", @@ -91,6 +76,15 @@ "fieldtype": "Check", "label": "Include holidays in Total no. of Working Days" }, + { + "fieldname": "max_working_hours_against_timesheet", + "fieldtype": "Float", + "label": "Max working hours against Timesheet" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { "default": "1", "description": "Emails salary slip to employee based on preferred email selected in Employee", @@ -115,15 +109,33 @@ "label": "Password Policy" }, { - "fieldname": "max_working_hours_against_timesheet", - "fieldtype": "Float", - "label": "Max working hours against Timesheet" - }, - { + "collapsible": 1, "fieldname": "leave_settings", "fieldtype": "Section Break", "label": "Leave Settings" }, + { + "fieldname": "leave_approval_notification_template", + "fieldtype": "Link", + "label": "Leave Approval Notification Template", + "options": "Email Template" + }, + { + "fieldname": "leave_status_notification_template", + "fieldtype": "Link", + "label": "Leave Status Notification Template", + "options": "Email Template" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "leave_approver_mandatory_in_leave_application", + "fieldtype": "Check", + "label": "Leave Approver Mandatory In Leave Application" + }, { "default": "0", "fieldname": "show_leaves_of_all_department_members_in_calendar", @@ -131,18 +143,22 @@ "label": "Show Leaves Of All Department Members In Calendar" }, { - "fieldname": "column_break_11", - "fieldtype": "Column Break" + "collapsible": 1, + "fieldname": "hiring_settings", + "fieldtype": "Section Break", + "label": "Hiring Settings" }, { - "fieldname": "column_break_18", - "fieldtype": "Column Break" + "default": "0", + "fieldname": "check_vacancies", + "fieldtype": "Check", + "label": "Check Vacancies On Job Offer Creation" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-05-31 16:18:50.245872", + "modified": "2019-07-01 18:59:55.256878", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", @@ -158,5 +174,6 @@ "write": 1 } ], + "sort_field": "modified", "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json index a78c9b2ddd..e9de393bc4 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.json +++ b/erpnext/hr/doctype/job_applicant/job_applicant.json @@ -39,7 +39,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, @@ -71,7 +71,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, @@ -96,7 +96,7 @@ "label": "Status", "length": 0, "no_copy": 0, - "options": "Open\nReplied\nRejected\nHold", + "options": "Open\nReplied\nRejected\nHold\nAccepted", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -346,7 +346,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:43.552049", + "modified": "2019-06-21 16:15:43.552049", "modified_by": "Administrator", "module": "HR", "name": "Job Applicant", diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py index 3ca862be85..6d275c82d9 100644 --- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py @@ -10,3 +10,14 @@ import unittest class TestJobApplicant(unittest.TestCase): pass + +def create_job_applicant(**args): + args = frappe._dict(args) + job_applicant = frappe.get_doc({ + "doctype": "Job Applicant", + "applicant_name": args.applicant_name or "_Test Applicant", + "email_id": args.email_id or "test_applicant@example.com", + "status": args.status or "Open" + }) + job_applicant.save() + return job_applicant \ No newline at end of file diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index 7e3014b38c..ef8004eedb 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -5,12 +5,56 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe import _ +from frappe.utils.data import get_link_to_form class JobOffer(Document): def onload(self): employee = frappe.db.get_value("Employee", {"job_applicant": self.job_applicant}, "name") or "" self.set_onload("employee", employee) + def validate(self): + self.validate_vacancies() + + def validate_vacancies(self): + staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) + check_vacancies = frappe.get_single("HR Settings").check_vacancies + if staffing_plan and check_vacancies: + vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies']) + job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)) + if vacancies - job_offers <= 0: + frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent))) + + def on_change(self): + update_job_applicant(self.status, self.job_applicant) + + def get_job_offer(self, from_date, to_date): + ''' Returns job offer created during a time period ''' + return frappe.get_all("Job Offer", filters={ + "offer_date": ['between', (from_date, to_date)], + "designation": self.designation, + "company": self.company + }, fields=['name']) + +def update_job_applicant(status, job_applicant): + if status in ("Accepted", "Rejected"): + frappe.set_value("Job Applicant", job_applicant, "status", status) + +def get_staffing_plan_detail(designation, company, offer_date): + detail = frappe.db.sql(""" + SELECT spd.name as name, + sp.from_date as from_date, + sp.to_date as to_date, + sp.name as parent + FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp + WHERE + sp.docstatus=1 + AND spd.designation=%s + AND sp.company=%s + AND %s between sp.from_date and sp.to_date + """, (designation, company, offer_date), as_dict=1) + return detail[0] if detail else None + @frappe.whitelist() def make_employee(source_name, target_doc=None): def set_missing_values(source, target): @@ -23,4 +67,3 @@ def make_employee(source_name, target_doc=None): }} }, target_doc, set_missing_values) return doc - diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py index c3aeb2b368..8886596450 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ b/erpnext/hr/doctype/job_offer/test_job_offer.py @@ -4,8 +4,78 @@ from __future__ import unicode_literals import frappe import unittest +from frappe.utils import nowdate, add_days +from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant +from erpnext.hr.doctype.designation.test_designation import create_designation +from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company # test_records = frappe.get_test_records('Job Offer') class TestJobOffer(unittest.TestCase): - pass + def test_job_offer_creation_against_vacancies(self): + create_staffing_plan(staffing_details=[{ + "designation": "Designer", + "vacancies": 0, + "estimated_cost_per_position": 5000 + }]) + frappe.db.set_value("HR Settings", None, "check_vacancies", 1) + job_applicant = create_job_applicant(email_id="test_job_offer@example.com") + job_offer = create_job_offer(job_applicant=job_applicant.name, designation="Researcher") + self.assertRaises(frappe.ValidationError, job_offer.submit) + + # test creation of job offer when vacancies are not present + frappe.db.set_value("HR Settings", None, "check_vacancies", 0) + job_offer.submit() + self.assertTrue(frappe.db.exists("Job Offer", job_offer.name)) + + def test_job_applicant_update(self): + create_staffing_plan() + job_applicant = create_job_applicant(email_id="test_job_applicants@example.com") + job_offer = create_job_offer(job_applicant=job_applicant.name) + job_offer.submit() + job_applicant.reload() + self.assertEquals(job_applicant.status, "Accepted") + + # status update after rejection + job_offer.status = "Rejected" + job_offer.submit() + job_applicant.reload() + self.assertEquals(job_applicant.status, "Rejected") + +def create_job_offer(**args): + args = frappe._dict(args) + if not args.job_applicant: + job_applicant = create_job_applicant() + + if not frappe.db.exists("Designation", args.designation): + designation = create_designation(designation_name=args.designation) + + job_offer = frappe.get_doc({ + "doctype": "Job Offer", + "job_applicant": args.job_applicant or job_applicant.name, + "offer_date": args.offer_date or nowdate(), + "designation": args.designation or "Researcher", + "status": args.status or "Accepted" + }) + return job_offer + +def create_staffing_plan(**args): + args = frappe._dict(args) + make_company() + frappe.db.set_value("Company", "_Test Company", "is_group", 1) + if frappe.db.exists("Staffing Plan", args.name or "Test"): + return + staffing_plan = frappe.get_doc({ + "doctype": "Staffing Plan", + "name": args.name or "Test", + "from_date": args.from_date or nowdate(), + "to_date": args.to_date or add_days(nowdate(), 10), + "staffing_details": args.staffing_details or [{ + "designation": "Researcher", + "vacancies": 1, + "estimated_cost_per_position": 50000 + }] + }) + staffing_plan.insert() + staffing_plan.submit() + return staffing_plan \ No newline at end of file diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.js b/erpnext/hr/doctype/staffing_plan/staffing_plan.js index 4fbc6b3089..04af2323c7 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.js +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.js @@ -5,7 +5,7 @@ frappe.ui.form.on('Staffing Plan', { setup: function(frm) { frm.set_query("designation", "staffing_details", function() { let designations = []; - $.each(frm.doc.staffing_details, function(index, staff_detail) { + (frm.doc.staffing_details || []).forEach(function(staff_detail) { if(staff_detail.designation){ designations.push(staff_detail.designation) } @@ -25,69 +25,63 @@ frappe.ui.form.on('Staffing Plan', { } }; }); - } + }, }); frappe.ui.form.on('Staffing Plan Detail', { designation: function(frm, cdt, cdn) { - let child = locals[cdt][cdn] - if(frm.doc.company && child.designation){ - frappe.call({ - "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.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); - } - } - }); + let child = locals[cdt][cdn]; + if(frm.doc.company && child.designation) { + set_number_of_positions(frm, cdt, cdn); } }, - number_of_positions: function(frm, cdt, cdn) { - set_vacancies(frm, cdt, cdn); + vacancies: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if(child.vacancies < child.current_openings) { + frappe.throw(__("Vacancies cannot be lower than the current openings")); + } + set_number_of_positions(frm, cdt, cdn); }, current_count: function(frm, cdt, cdn) { - set_vacancies(frm, cdt, cdn); + set_number_of_positions(frm, cdt, cdn); }, estimated_cost_per_position: function(frm, cdt, cdn) { - let child = locals[cdt][cdn]; set_total_estimated_cost(frm, cdt, cdn); } - }); -var set_vacancies = function(frm, cdt, cdn) { - let child = locals[cdt][cdn] - 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); - } - +var set_number_of_positions = function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (!child.designation) frappe.throw(__("Please enter the designation")); + frappe.call({ + "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.employee_count); + frappe.model.set_value(cdt, cdn, 'current_openings', data.message.job_openings); + let total_positions = cint(data.message.employee_count) + cint(child.vacancies); + if (cint(child.number_of_positions) < total_positions){ + frappe.model.set_value(cdt, cdn, 'number_of_positions', total_positions); + } + } + else{ // No employees for this designation + frappe.model.set_value(cdt, cdn, 'current_count', 0); + frappe.model.set_value(cdt, cdn, 'current_openings', 0); + } + } + }); + refresh_field("staffing_details"); 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.vacancies > 0 && child.estimated_cost_per_position) { @@ -102,11 +96,11 @@ var set_total_estimated_cost = function(frm, cdt, cdn) { var set_total_estimated_budget = function(frm) { let estimated_budget = 0.0 if(frm.doc.staffing_details) { - $.each(frm.doc.staffing_details, function(index, staff_detail) { + (frm.doc.staffing_details || []).forEach(function(staff_detail) { if(staff_detail.total_estimated_cost){ estimated_budget += staff_detail.total_estimated_cost } }) frm.set_value('total_estimated_budget', estimated_budget); } -} +} \ No newline at end of file diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py index 83e53135ef..e6afbcc220 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py @@ -13,41 +13,39 @@ class ParentCompanyError(frappe.ValidationError): pass class StaffingPlan(Document): def validate(self): + self.validate_period() + self.validate_details() + self.set_total_estimated_budget() + + def validate_period(self): # Validate Dates 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")) - self.total_estimated_budget = 0 - + def validate_details(self): for detail in self.get("staffing_details"): - self.set_vacancies(detail) self.validate_overlap(detail) self.validate_with_subsidiary_plans(detail) self.validate_with_parent_plan(detail) + def set_total_estimated_budget(self): + self.total_estimated_budget = 0 + + for detail in self.get("staffing_details"): #Set readonly fields + self.set_number_of_positions(detail) 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.number_of_positions > 0: 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 + detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position) + self.total_estimated_budget += detail.total_estimated_cost - def set_vacancies(self, row): - if not row.vacancies: - current_openings = 0 - for field in ['current_count', 'current_openings']: - if row.get(field): - current_openings += row.get(field) - - row.vacancies = row.number_of_positions - current_openings + def set_number_of_positions(self, detail): + detail.number_of_positions = cint(detail.vacancies) + cint(detail.current_count) def validate_overlap(self, staffing_plan_detail): # Validate if any submitted Staffing Plan exist for any Designations in this plan @@ -132,19 +130,24 @@ def get_designation_counts(designation, company): if not designation: return False - employee_counts_dict = {} - lft, rgt = frappe.get_cached_value('Company', company, ["lft", "rgt"]) - 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] + employee_counts = {} + company_set = get_company_set(company) - 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] + employee_counts["employee_count"] = frappe.db.get_value("Employee", + filters={ + 'designation': designation, + 'status': 'Active', + 'company': ('in', company_set) + }, fieldname=['count(name)']) - return employee_counts_dict + employee_counts['job_openings'] = frappe.db.get_value("Job Opening", + filters={ + 'designation': designation, + 'status': 'Open', + 'company': ('in', company_set) + }, fieldname=['count(name)']) + + return employee_counts @frappe.whitelist() def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())): @@ -165,3 +168,13 @@ def get_active_staffing_plan_details(company, designation, from_date=getdate(now # Only a single staffing plan can be active for a designation on given date return staffing_plan if staffing_plan else None + +def get_company_set(company): + return frappe.db.sql_list(""" + SELECT + name + FROM `tabCompany` + WHERE + parent_company=%(company)s + OR name=%(company)s + """, (dict(company=company))) \ No newline at end of file diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py index 22dba99af0..4a0ce1800a 100644 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py @@ -24,7 +24,7 @@ class TestStaffingPlan(unittest.TestCase): staffing_plan.to_date = add_days(nowdate(), 10) staffing_plan.append("staffing_details", { "designation": "Designer", - "number_of_positions": 6, + "vacancies": 6, "estimated_cost_per_position": 50000 }) staffing_plan.insert() @@ -42,7 +42,7 @@ class TestStaffingPlan(unittest.TestCase): staffing_plan.to_date = add_days(nowdate(), 10) staffing_plan.append("staffing_details", { "designation": "Designer", - "number_of_positions": 3, + "vacancies": 3, "estimated_cost_per_position": 45000 }) self.assertRaises(SubsidiaryCompanyError, staffing_plan.insert) @@ -58,7 +58,7 @@ class TestStaffingPlan(unittest.TestCase): staffing_plan.to_date = add_days(nowdate(), 10) staffing_plan.append("staffing_details", { "designation": "Designer", - "number_of_positions": 7, + "vacancies": 7, "estimated_cost_per_position": 50000 }) staffing_plan.insert() @@ -73,7 +73,7 @@ class TestStaffingPlan(unittest.TestCase): staffing_plan.to_date = add_days(nowdate(), 10) staffing_plan.append("staffing_details", { "designation": "Designer", - "number_of_positions": 7, + "vacancies": 7, "estimated_cost_per_position": 60000 }) staffing_plan.insert() @@ -93,4 +93,4 @@ def make_company(): company.parent_company = "_Test Company" company.default_currency = "INR" company.country = "India" - company.insert() + company.insert() \ No newline at end of file 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 f1d16096c0..77164c4e67 100644 --- a/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json +++ b/erpnext/hr/doctype/staffing_plan_detail/staffing_plan_detail.json @@ -1,297 +1,79 @@ { - "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", + "field_order": [ + "designation", + "vacancies", + "estimated_cost_per_position", + "total_estimated_cost", + "column_break_5", + "current_count", + "current_openings", + "number_of_positions" + ], "fields": [ { - "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 + "reqd": 1 }, { - "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 + "read_only": 1 }, { - "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 + "label": "Estimated Cost Per Position" }, { - "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 + "fieldtype": "Column Break" }, { - "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 + "read_only": 1 }, { - "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 + "read_only": 1 }, { - "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 - }, - { - "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 - } + "label": "Vacancies" + }, + { + "fieldname": "total_estimated_cost", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Estimated Cost", + "read_only": 1 + } ], - "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": "2019-06-24 18:40:37.140178", "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 -} + "track_changes": 1 +} \ No newline at end of file