feat: Employee Referral (#24997)

* feat: Employee referal design and status sync

* feat: create job Applicant and linked Document

* feat: Added list view indicator

* chore: formatted file

* feat: Addedd Employee Referral form Dashboard

* feat: pay compensation via additional salary

* test: Employee Rreferral

* Update erpnext/hr/doctype/employee_advance/employee_advance.js

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>

* fix: changes requested

* fix: changes requested

* fix: sider

* fix: translation

* fix: test

* feat: added to Dashboard

* fix: changes

* chore: clean-up Employee Referral form

- fix labels

- fix full name field

- fix failing test

- set title for Employee Referral form

- set fields in List View and Standard Filter

* feat: option to add a resume link

- copy resume and resume link from Employee Referral to Job Applicant

* fix: multiple fixes

- confirm before rejecting employee referral

- set primary actions for custom buttons

- Validations for additional salary against employee referrals with status as accepted only

- fix list view indicators

- code formatting style

* feat: Add field to track Referral Bonus Payment Status

- fix visibility of additional salary button

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js
index 5037ceb..fa4b06a 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance.js
+++ b/erpnext/hr/doctype/employee_advance/employee_advance.js
@@ -34,7 +34,7 @@
 			};
 		});
 
-		frm.set_query('salary_component', function(doc) {
+		frm.set_query('salary_component', function() {
 			return {
 				filters: {
 					"type": "Deduction"
@@ -44,48 +44,49 @@
 	},
 
 	refresh: function(frm) {
-		if (frm.doc.docstatus===1
-			&& (flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount))
-			&& frappe.model.can_create("Payment Entry")) {
+		if (frm.doc.docstatus === 1 &&
+			(flt(frm.doc.paid_amount) < flt(frm.doc.advance_amount)) &&
+			frappe.model.can_create("Payment Entry")) {
 			frm.add_custom_button(__('Payment'),
-				function() { frm.events.make_payment_entry(frm); }, __('Create'));
-		}
-		else if (
-			frm.doc.docstatus === 1
-			&& flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount)
-			&& frappe.model.can_create("Expense Claim")
+				function () {
+					frm.events.make_payment_entry(frm);
+				}, __('Create'));
+		} else if (
+			frm.doc.docstatus === 1 &&
+			flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) &&
+			frappe.model.can_create("Expense Claim")
 		) {
 			frm.add_custom_button(
 				__("Expense Claim"),
-				function() {
+				function () {
 					frm.events.make_expense_claim(frm);
 				},
 				__('Create')
 			);
 		}
 
-		if (frm.doc.docstatus === 1
-			&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) {
+		if (frm.doc.docstatus === 1 &&
+			(flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) && flt(frm.doc.paid_amount) != flt(frm.doc.return_amount))) {
 
-			if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")){
-				frm.add_custom_button(__("Return"),  function() {
+			if (frm.doc.repay_unclaimed_amount_from_salary == 0 && frappe.model.can_create("Journal Entry")) {
+				frm.add_custom_button(__("Return"), function() {
 					frm.trigger('make_return_entry');
 				}, __('Create'));
-			}else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")){
-				frm.add_custom_button(__("Deduction from salary"),  function() {
+			} else if (frm.doc.repay_unclaimed_amount_from_salary == 1 && frappe.model.can_create("Additional Salary")) {
+				frm.add_custom_button(__("Deduction from salary"), function() {
 					frm.events.make_deduction_via_additional_salary(frm);
 				}, __('Create'));
 			}
 		}
 	},
 
-	make_deduction_via_additional_salary: function(frm){
+	make_deduction_via_additional_salary: function(frm) {
 		frappe.call({
 			method: "erpnext.hr.doctype.employee_advance.employee_advance.create_return_through_additional_salary",
 			args: {
 				doc: frm.doc
 			},
-			callback: function (r){
+			callback: function(r) {
 				var doclist = frappe.model.sync(r.message);
 				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
 			}
@@ -94,7 +95,7 @@
 
 	make_payment_entry: function(frm) {
 		var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
-		if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
+		if (frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
 			method = "erpnext.hr.doctype.employee_advance.employee_advance.make_bank_entry";
 		}
 		return frappe.call({
@@ -148,11 +149,11 @@
 		});
 	},
 
-	employee: function (frm) {
+	employee: function(frm) {
 		if (frm.doc.employee) {
 			frappe.run_serially([
-				() => 	frm.trigger('get_employee_currency'),
-				() => 	frm.trigger('get_pending_amount')
+				() => frm.trigger('get_employee_currency'),
+				() => frm.trigger('get_pending_amount')
 			]);
 		}
 	},
@@ -199,7 +200,7 @@
 			} else {
 				frm.set_value("exchange_rate", 1.0);
 				frm.set_df_property('exchange_rate', 'hidden', 1);
-				frm.set_df_property("exchange_rate", "description", "" );
+				frm.set_df_property("exchange_rate", "description", "");
 			}
 			frm.refresh_fields();
 		}
@@ -215,8 +216,8 @@
 			callback: function(r) {
 				frm.set_value("exchange_rate", flt(r.message));
 				frm.set_df_property('exchange_rate', 'hidden', 0);
-				frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
-					+ " = [?] " + company_currency);
+				frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency +
+					" = [?] " + company_currency);
 			}
 		});
 	}
diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
index c3b4a3a..2f493e2 100644
--- a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
+++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py
@@ -4,10 +4,10 @@
 def get_data():
 	return {
 		'fieldname': 'employee_advance',
-        'non_standard_fieldnames': {
-            'Payment Entry': 'reference_name',
-            'Journal Entry': 'reference_name'
-        },
+		'non_standard_fieldnames': {
+			'Payment Entry': 'reference_name',
+			'Journal Entry': 'reference_name'
+		},
 		'transactions': [
 			{
 				'items': ['Expense Claim']
diff --git a/erpnext/hr/doctype/employee_referral/__init__.py b/erpnext/hr/doctype/employee_referral/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/__init__.py
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.js b/erpnext/hr/doctype/employee_referral/employee_referral.js
new file mode 100644
index 0000000..9c99bbb
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.js
@@ -0,0 +1,68 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Employee Referral", {
+	refresh: function(frm) {
+		if (frm.doc.docstatus === 1 && frm.doc.status === "Pending") {
+			frm.add_custom_button(__("Reject Employee Referral"), function() {
+				frappe.confirm(
+					__("Are you sure you want to reject the Employee Referral?"),
+					function() {
+						frm.doc.status = "Rejected";
+						frm.dirty();
+						frm.save_or_update();
+					},
+					function() {
+						window.close();
+					}
+				);
+			});
+
+			frm.add_custom_button(__("Create Job Applicant"), function() {
+				frm.events.create_job_applicant(frm);
+			}).addClass("btn-primary");
+		}
+
+		// To check whether Payment is done or not
+		if (frm.doc.docstatus === 1 && frm.doc.status === "Accepted") {
+			frappe.db.get_list("Additional Salary", {
+				filters: {
+					ref_docname: cur_frm.doc.name,
+					docstatus: 1
+				},
+				fields: ["count(name) as additional_salary_count"]
+			}).then((data) => {
+
+				let additional_salary_count = data[0].additional_salary_count;
+
+				if (frm.doc.is_applicable_for_referral_bonus && !additional_salary_count) {
+					frm.add_custom_button(__("Create Additional Salary"), function() {
+						frm.events.create_additional_salary(frm);
+					}).addClass("btn-primary");
+				}
+			});
+		}
+
+
+
+	},
+	create_job_applicant: function(frm) {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.hr.doctype.employee_referral.employee_referral.create_job_applicant",
+			frm: frm
+		});
+	},
+
+	create_additional_salary: function(frm) {
+		frappe.call({
+			method: "erpnext.hr.doctype.employee_referral.employee_referral.create_additional_salary",
+			args: {
+				doc: frm.doc
+			},
+			callback: function (r) {
+				var doclist = frappe.model.sync(r.message);
+				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+			}
+		});
+	},
+});
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.json b/erpnext/hr/doctype/employee_referral/employee_referral.json
new file mode 100644
index 0000000..bfd404b
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.json
@@ -0,0 +1,294 @@
+{
+ "actions": [],
+ "autoname": "format:HR-REF-{####}",
+ "creation": "2021-03-23 14:54:45.047051",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "first_name",
+  "last_name",
+  "full_name",
+  "email",
+  "contact_no",
+  "resume",
+  "resume_link",
+  "column_break_6",
+  "date",
+  "status",
+  "for_designation",
+  "current_employer",
+  "current_job_title",
+  "referrer_details_section",
+  "referrer",
+  "referrer_name",
+  "column_break_14",
+  "is_applicable_for_referral_bonus",
+  "referral_payment_status",
+  "department",
+  "additional_information_section",
+  "qualification_reason",
+  "work_references",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "first_name",
+   "fieldtype": "Data",
+   "label": "First Name ",
+   "reqd": 1
+  },
+  {
+   "fieldname": "last_name",
+   "fieldtype": "Data",
+   "label": "Last Name",
+   "reqd": 1
+  },
+  {
+   "fieldname": "full_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Full Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "contact_no",
+   "fieldtype": "Data",
+   "in_standard_filter": 1,
+   "label": "Contact No.",
+   "options": "Phone"
+  },
+  {
+   "fieldname": "current_employer",
+   "fieldtype": "Data",
+   "label": "Current Employer "
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "in_standard_filter": 1,
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_standard_filter": 1,
+   "label": "Status",
+   "no_copy": 1,
+   "options": "Pending\nIn Process\nAccepted\nRejected",
+   "permlevel": 1,
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "current_job_title",
+   "fieldtype": "Data",
+   "label": "Current Job Title"
+  },
+  {
+   "fieldname": "resume",
+   "fieldtype": "Attach",
+   "label": "Resume"
+  },
+  {
+   "fieldname": "referrer_details_section",
+   "fieldtype": "Section Break",
+   "label": "Referrer Details"
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fieldname": "additional_information_section",
+   "fieldtype": "Section Break",
+   "label": "Additional Information "
+  },
+  {
+   "fieldname": "work_references",
+   "fieldtype": "Text Editor",
+   "label": "Work References"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Referral",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "for_designation",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "For Designation ",
+   "options": "Designation",
+   "reqd": 1
+  },
+  {
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Email",
+   "options": "Email",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "is_applicable_for_referral_bonus",
+   "fieldtype": "Check",
+   "label": "Is Applicable for Referral Bonus"
+  },
+  {
+   "fieldname": "qualification_reason",
+   "fieldtype": "Text Editor",
+   "label": "Why is this Candidate Qualified for this Position?"
+  },
+  {
+   "fieldname": "referrer",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Referrer",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "referrer.employee_name",
+   "fieldname": "referrer_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Referrer Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "resume_link",
+   "fieldtype": "Data",
+   "label": "Resume Link"
+  },
+  {
+   "fieldname": "referral_payment_status",
+   "fieldtype": "Select",
+   "label": "Referral Bonus Payment Status",
+   "options": "\nUnpaid\nPaid",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-04-26 21:21:38.094086",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Referral",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "full_name"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py
new file mode 100644
index 0000000..45d6872
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import get_link_to_form
+from frappe.model.document import Document
+
+class EmployeeReferral(Document):
+	def validate(self):
+		self.set_full_name()
+		self.set_referral_bonus_payment_status()
+
+	def set_full_name(self):
+		self.full_name = " ".join(filter(None, [self.first_name, self.last_name]))
+
+	def set_referral_bonus_payment_status(self):
+		if not self.is_applicable_for_referral_bonus:
+			self.referral_payment_status = ""
+		else:
+			if not self.referral_payment_status:
+				self.referral_payment_status = "Unpaid"
+
+
+@frappe.whitelist()
+def create_job_applicant(source_name, target_doc=None):
+	emp_ref = frappe.get_doc("Employee Referral", source_name)
+	#just for Api call if some set status apart from default Status
+	status = emp_ref.status
+	if emp_ref.status in ["Pending", "In process"]:
+		status = "Open"
+
+	job_applicant = frappe.new_doc("Job Applicant")
+	job_applicant.employee_referral = emp_ref.name
+	job_applicant.status = status
+	job_applicant.applicant_name = emp_ref.full_name
+	job_applicant.email_id = emp_ref.email
+	job_applicant.phone_number = emp_ref.contact_no
+	job_applicant.resume_attachment = emp_ref.resume
+	job_applicant.resume_link = emp_ref.resume_link
+	job_applicant.save()
+
+	frappe.msgprint(_("Job Applicant {0} created successfully.").format(
+		get_link_to_form("Job Applicant", job_applicant.name)),
+		title=_("Success"), indicator="green")
+
+	emp_ref.db_set("status", "In Process")
+
+	return job_applicant
+
+
+@frappe.whitelist()
+def create_additional_salary(doc):
+	import json
+	from six import string_types
+
+	if isinstance(doc, string_types):
+		doc = frappe._dict(json.loads(doc))
+
+	if not frappe.db.exists("Additional Salary", {"ref_docname": doc.name}):
+		additional_salary = frappe.new_doc("Additional Salary")
+		additional_salary.employee = doc.referrer
+		additional_salary.company = frappe.db.get_value("Employee", doc.referrer, "company")
+		additional_salary.overwrite_salary_structure_amount = 0
+		additional_salary.ref_doctype = doc.doctype
+		additional_salary.ref_docname = doc.name
+
+	return additional_salary
+
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
new file mode 100644
index 0000000..afa2a1f
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+
+def get_data():
+	return {
+		'fieldname': 'employee_referral',
+		'non_standard_fieldnames': {
+			'Additional Salary': 'ref_docname'
+		},
+		'transactions': [
+			{
+				'items': ['Job Applicant', 'Additional Salary']
+			},
+
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_list.js b/erpnext/hr/doctype/employee_referral/employee_referral_list.js
new file mode 100644
index 0000000..7533ab6
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/employee_referral_list.js
@@ -0,0 +1,14 @@
+frappe.listview_settings['Employee Referral'] = {
+	add_fields: ["status"],
+	get_indicator: function (doc) {
+		if (doc.status == "Pending") {
+			return [__(doc.status), "grey", "status,=," + doc.status];
+		} else if (doc.status == "In Process") {
+			return [__(doc.status), "orange", "status,=," + doc.status];
+		} else if (doc.status == "Accepted") {
+			return [__(doc.status), "green", "status,=," + doc.status];
+		} else if (doc.status == "Rejected") {
+			return [__(doc.status), "red", "status,=," + doc.status];
+		}
+	},
+};
\ No newline at end of file
diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
new file mode 100644
index 0000000..a674f39
--- /dev/null
+++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+from frappe.utils import today
+from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.employee_referral.employee_referral import create_job_applicant, create_additional_salary
+from erpnext.hr.doctype.employee.test_employee import make_employee
+import unittest
+
+class TestEmployeeReferral(unittest.TestCase):
+	def test_workflow_and_status_sync(self):
+		emp_ref = create_employee_referral()
+
+		#Check Initial status
+		self.assertTrue(emp_ref.status, "Pending")
+
+		job_applicant = create_job_applicant(emp_ref.name)
+
+
+		#Check status sync
+		emp_ref.reload()
+		self.assertTrue(emp_ref.status, "In Process")
+
+		job_applicant.reload()
+		job_applicant.status = "Rejected"
+		job_applicant.save()
+
+		emp_ref.reload()
+		self.assertTrue(emp_ref.status, "Rejected")
+
+		job_applicant.reload()
+		job_applicant.status = "Accepted"
+		job_applicant.save()
+
+		emp_ref.reload()
+		self.assertTrue(emp_ref.status, "Accepted")
+
+
+		# Check for Referral reference in additional salary
+
+		add_sal = create_additional_salary(emp_ref)
+		self.assertTrue(add_sal.ref_docname, emp_ref.name)
+
+
+def create_employee_referral():
+	emp_ref = frappe.new_doc("Employee Referral")
+	emp_ref.first_name = "Mahesh"
+	emp_ref.last_name = "Singh"
+	emp_ref.email = "a@b.c"
+	emp_ref.date = today()
+	emp_ref.for_designation = create_designation().name
+	emp_ref.referrer = make_employee("testassetmovemp@example.com", company="_Test Company")
+	emp_ref.is_applicable_for_employee_referral_compensation = 1
+	emp_ref.save()
+	emp_ref.submit()
+
+	return emp_ref
\ 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 1360fd1..bcea5f5 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.json
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.json
@@ -18,6 +18,7 @@
   "job_title",
   "source",
   "source_name",
+  "employee_referral",
   "applicant_rating",
   "section_break_6",
   "notes",
@@ -152,13 +153,20 @@
    "fieldtype": "Link",
    "label": "Currency",
    "options": "Currency"
+  },
+  {
+   "fieldname": "employee_referral",
+   "fieldtype": "Link",
+   "label": "Employee Referral",
+   "options": "Employee Referral",
+   "read_only": 1
   }
  ],
  "icon": "fa fa-user",
  "idx": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-09-18 12:39:02.557563",
+ "modified": "2021-03-24 15:51:11.117517",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Job Applicant",
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py
index a6aef04..0594ba3 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -28,10 +28,21 @@
 		if self.email_id:
 			validate_email_address(self.email_id, True)
 
+		if self.employee_referral:
+			self.set_status_for_employee_referral()
+
 		if not self.applicant_name and self.email_id:
 			guess = self.email_id.split('@')[0]
 			self.applicant_name = ' '.join([p.capitalize() for p in guess.split('.')])
 
+	def set_status_for_employee_referral(self):
+		emp_ref = frappe.get_doc("Employee Referral", self.employee_referral)
+		if self.status in ["Open", "Replied", "Hold"]:
+			emp_ref.db_set("status", "In Process")
+		elif self.status in ["Accepted", "Rejected"]:
+			emp_ref.db_set("status", self.status)
+
+
 	def check_email_id_is_unique(self):
 		if self.email_id:
 			names = frappe.db.sql_list("""select name from `tabJob Applicant`
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index f4b56a0..c5201c2 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -521,6 +521,15 @@
    "type": "Link"
   },
   {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Referral",
+   "link_to": "Employee Referral",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -814,7 +823,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-03-24 17:35:21.483297",
+ "modified": "2021-04-26 13:36:15.413819",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR",
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index 13b6c05..ebeddf9 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -13,12 +13,19 @@
 		if self.ref_doctype == "Employee Advance" and self.ref_docname:
 			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
 
+		self.update_employee_referral()
+
+	def on_cancel(self):
+		self.update_employee_referral(cancel=True)
+
 	def validate(self):
 		self.validate_dates()
 		self.validate_salary_structure()
 		self.validate_recurring_additional_salary_overlap()
+		self.validate_employee_referral()
+
 		if self.amount < 0:
-			frappe.throw(_("Amount should not be less than zero."))
+			frappe.throw(_("Amount should not be less than zero"))
 
 	def validate_salary_structure(self):
 		if not frappe.db.exists('Salary Structure Assignment', {'employee': self.employee}):
@@ -70,6 +77,27 @@
 			if self.payroll_date and getdate(self.payroll_date) > getdate(relieving_date):
 				frappe.throw(_("Payroll date can not be greater than employee's relieving date."))
 
+	def validate_employee_referral(self):
+		if self.ref_doctype == "Employee Referral":
+			referral_details = frappe.db.get_value("Employee Referral", self.ref_docname,
+				["is_applicable_for_referral_bonus", "status"], as_dict=1)
+
+			if not referral_details.is_applicable_for_referral_bonus:
+				frappe.throw(_("Employee Referral {0} is not applicable for referral bonus.").format(
+					self.ref_docname))
+
+			if self.type == "Deduction":
+				frappe.throw(_("Earning Salary Component is required for Employee Referral Bonus."))
+
+			if referral_details.status != "Accepted":
+				frappe.throw(_("Additional Salary for referral bonus can only be created against Employee Referral with status {0}").format(
+					frappe.bold("Accepted")))
+
+	def update_employee_referral(self, cancel=False):
+		if self.ref_doctype == "Employee Referral":
+			status = "Unpaid" if cancel else "Paid"
+			frappe.db.set_value("Employee Referral", self.ref_docname, "referral_payment_status", status)
+
 	def get_amount(self, sal_start_date, sal_end_date):
 		start_date = getdate(sal_start_date)
 		end_date = getdate(sal_end_date)
@@ -110,8 +138,7 @@
 	for d in additional_salary_list:
 		if d.overwrite:
 			if d.component in components_to_overwrite:
-				frappe.throw(_("Multiple Additional Salaries with overwrite "
-					"property exist for Salary Component {0} between {1} and {2}.").format(
+				frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component {0} between {1} and {2}.").format(
 					frappe.bold(d.component), start_date, end_date), title=_("Error"))
 
 			components_to_overwrite.append(d.component)