Skip to content

Commit

Permalink
Merge pull request #1166 from frappe/mergify/copy/version-15-hotfix/p…
Browse files Browse the repository at this point in the history
…r-835

feat: Job Portal Enhancements (copy #835)
  • Loading branch information
ruchamahabal authored Dec 15, 2023
2 parents 053b8de + c1e1e8a commit 0e07f30
Show file tree
Hide file tree
Showing 12 changed files with 918 additions and 89 deletions.
2 changes: 1 addition & 1 deletion hrms/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
website_generators = ["Job Opening"]

website_route_rules = [
{"from_route": "/jobs", "to_route": "Job Opening"},
{"from_route": "/hrms/<path:app_path>", "to_route": "hrms"},
]
# Jinja
Expand Down Expand Up @@ -215,6 +214,7 @@
"hrms.controllers.employee_reminders.send_work_anniversary_reminders",
"hrms.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary",
"hrms.hr.doctype.interview.interview.send_daily_feedback_reminder",
"hrms.hr.doctype.job_opening.job_opening.close_expired_job_openings",
],
"daily_long": [
"hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation",
Expand Down
5 changes: 3 additions & 2 deletions hrms/hr/doctype/job_applicant/job_applicant.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Job Opening",
"options": "Job Opening"
"options": "Job Opening",
"search_index": 1
},
{
"fieldname": "source",
Expand Down Expand Up @@ -193,7 +194,7 @@
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-09-14 16:50:39.316079",
"modified": "2023-09-14 17:03:48.838409",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Applicant",
Expand Down
84 changes: 77 additions & 7 deletions hrms/hr/doctype/job_opening/job_opening.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
{
"actions": [],
"allow_import": 1,
"autoname": "field:route",
"autoname": "HR-OPN-.YYYY.-.#### ",
"creation": "2013-01-15 16:13:36",
"description": "Description of a Job Opening",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"job_details_section",
"job_title",
"company",
"status",
"column_break_5",
"designation",
"column_break_5",
"status",
"posted_on",
"closes_on",
"closed_on",
"section_break_nngy",
"company",
"department",
"column_break_dxpv",
"employment_type",
"location",
"references_section",
"staffing_plan",
"planned_vacancies",
Expand All @@ -31,6 +39,7 @@
"lower_range",
"upper_range",
"column_break_20",
"salary_per",
"publish_salary_range"
],
"fields": [
Expand Down Expand Up @@ -181,16 +190,67 @@
"fieldtype": "Int",
"label": "Vacancies",
"read_only": 1
},
{
"default": "Now",
"fieldname": "posted_on",
"fieldtype": "Datetime",
"label": "Posted On"
},
{
"depends_on": "eval:doc.status == 'Open'",
"description": "If set, the job opening will be closed automatically after this date",
"fieldname": "closes_on",
"fieldtype": "Date",
"label": "Closes On"
},
{
"fieldname": "employment_type",
"fieldtype": "Link",
"label": "Employment Type",
"options": "Employment Type"
},
{
"fieldname": "location",
"fieldtype": "Link",
"label": "Location",
"options": "Branch"
},
{
"fieldname": "section_break_nngy",
"fieldtype": "Section Break",
"label": "Company Details"
},
{
"fieldname": "column_break_dxpv",
"fieldtype": "Column Break"
},
{
"fieldname": "job_details_section",
"fieldtype": "Section Break"
},
{
"depends_on": "eval:doc.status == 'Closed'",
"fieldname": "closed_on",
"fieldtype": "Date",
"label": "Closed On"
},
{
"default": "Month",
"fieldname": "salary_per",
"fieldtype": "Select",
"label": "Salary Paid Per",
"options": "Month\nYear"
}
],
"icon": "fa fa-bookmark",
"idx": 1,
"links": [],
"modified": "2023-09-14 16:50:39.316079",
"modified": "2023-09-15 13:41:54.111743",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Opening",
"naming_rule": "By fieldname",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
Expand All @@ -211,5 +271,15 @@
],
"sort_field": "modified",
"sort_order": "ASC",
"states": []
"states": [
{
"color": "Green",
"title": "Open"
},
{
"color": "Gray",
"title": "Closed"
}
],
"title_field": "job_title"
}
83 changes: 50 additions & 33 deletions hrms/hr/doctype/job_opening/job_opening.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

import frappe
from frappe import _
from frappe.utils import get_link_to_form, getdate
from frappe.model.naming import set_name_from_naming_options
from frappe.utils import get_link_to_form, getdate, pretty_date
from frappe.website.website_generator import WebsiteGenerator

from hrms.hr.doctype.staffing_plan.staffing_plan import (
Expand All @@ -22,12 +23,40 @@ class JobOpening(WebsiteGenerator):
page_title_field="job_title",
)

def autoname(self):
self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self)

def validate(self):
if not self.route:
self.route = frappe.scrub(self.job_title).replace("_", "-")
self.route = (
f"jobs/{frappe.scrub(self.company)}/{frappe.scrub(self.job_title).replace('_', '-')}"
)
self.update_closing_date()
self.validate_dates()
self.validate_current_vacancies()

def on_update(self):
self.update_job_requisition_status()

def update_closing_date(self):
old_doc = self.get_doc_before_save()
if not old_doc:
return

if old_doc.status == "Open" and self.status == "Closed":
self.closes_on = None
if not self.closed_on:
self.closed_on = getdate()

elif old_doc.status == "Closed" and self.status == "Open":
self.closed_on = None

def validate_dates(self):
if self.status == "Open":
self.validate_from_to_dates("posted_on", "closes_on")
if self.status == "Closed":
self.validate_from_to_dates("posted_on", "closed_on")

def validate_current_vacancies(self):
if not self.staffing_plan:
staffing_plan = get_active_staffing_plan_details(self.company, self.designation)
Expand Down Expand Up @@ -73,38 +102,26 @@ def update_job_requisition_status(self):
job_requisition.save()

def get_context(self, context):
context.no_of_applications = frappe.db.count("Job Applicant", {"job_title": self.name})
context.parents = [{"route": "jobs", "title": _("All Jobs")}]
context.posted_on = pretty_date(self.posted_on)


def get_list_context(context):
context.title = _("Jobs")
context.introduction = _("Current Job Openings")
context.get_list = get_job_openings


def get_job_openings(
doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None
):
fields = [
"name",
"status",
"job_title",
"description",
"publish_salary_range",
"lower_range",
"upper_range",
"currency",
"job_application_route",
]

filters = filters or {}
filters.update({"status": "Open"})

if txt:
filters.update(
{"job_title": ["like", "%{0}%".format(txt)], "description": ["like", "%{0}%".format(txt)]}
)
def close_expired_job_openings():
today = getdate()

return frappe.get_all(
doctype, filters, fields, start=limit_start, page_length=limit_page_length, order_by=order_by
)
Opening = frappe.qb.DocType("Job Opening")
openings = (
frappe.qb.from_(Opening)
.select(Opening.name)
.where(
(Opening.status == "Open") & (Opening.closes_on.isnotnull()) & (Opening.closes_on < today)
)
).run(pluck=True)

for d in openings:
doc = frappe.get_doc("Job Opening", d)
doc.status = "Closed"
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.save()
18 changes: 0 additions & 18 deletions hrms/hr/doctype/job_opening/templates/job_opening_row.html

This file was deleted.

20 changes: 20 additions & 0 deletions hrms/hr/doctype/job_opening/test_job_opening.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from erpnext.setup.doctype.employee.test_employee import make_employee

from hrms.hr.doctype.job_opening.job_opening import close_expired_job_openings
from hrms.hr.doctype.staffing_plan.test_staffing_plan import make_company


Expand Down Expand Up @@ -55,6 +56,25 @@ def test_vacancies_fulfilled(self):
opening_1.status = "Closed"
opening_1.save()

def test_close_expired_job_openings(self):
today = getdate()

opening_1 = get_job_opening()
opening_1.posted_on = add_days(today, -2)
opening_1.closes_on = add_days(today, -1)
opening_1.insert()

opening_2 = get_job_opening(job_title="Designer New")
opening_2.insert()

close_expired_job_openings()
opening_1.reload()
opening_2.reload()

self.assertEqual(opening_1.status, "Closed")
self.assertEqual(opening_1.closed_on, today)
self.assertEqual(opening_2.status, "Open")


def get_job_opening(**args):
args = frappe._dict(args)
Expand Down
Loading

0 comments on commit 0e07f30

Please sign in to comment.