feat: Tracking Multi-round interview (#25482) (#27724)

* feat: Tracking Multi-round interview

* fix: releted to scheduler event and formating

* fix: job applicant UI/UX and conflicts

* test: Interview Round

* fix(test): Employee referral, Employee Onboarding, Job Offer

* fix: sider

* feat: set default value in Hr settings

* feat: added validation for designation

* test: Interview

* test: Added validatiolns for skill

* test: Interview feedback

* fix: sider

* fix: remove unnecessary validations and form label cleanups

* chore: clean-up Interview Round and Interview Type doctype

* fix: remove redundant Rating Value, only keep Rating

* fix: update interview details on feedback submission

- make interview feedback submission dialog minimizable

* fix: show submit feedback button only if feedback doesn't exist

* refactor: Interview and Feedback statuses and workflow

* fix(HR Settings): clean up interview settings

* refactor: Interview

* refactor: Interview Feedback, remove unnecessary validations

* chore: update notification messages

* chore: remove unnecessary formatting changes in attendance list and leave application

* refactor: Job Applicant to Interview mapping

* chore: sorted imports

* chore: sorted imports

* fix: sider issues

* fix: linter issues

* fix: sider issues

* fix: tests

* fix: sorted imports

* fix: tests, sider

* fix: therapy plan test

* fix: sider issues

* feat: Include From Time and To Time fields in Interview for cleaner data

* feat: Interview Calendar

* fix: allow renaming masters

* fix: add more fields to list view and standard filter

* fix: validate overlapping interviews

* fix: update tests

* fix: linter issues

* refactor: replace reminder messages with Email Templates

* fix: sider issues

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
(cherry picked from commit 57e66f958cd57d66a6fd3b19f6cd3593eab63666)

Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com>
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
index 099146c..9dd97a6 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
+++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
@@ -21,6 +21,7 @@
 	def setUp(self):
 		frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
 		frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+		frappe.db.sql('delete from `tabPatient Appointment`')
 		make_pos_profile()
 
 	def test_medical_record(self):
diff --git a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
index 4f96f6a..021ba9b 100644
--- a/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/test_therapy_plan.py
@@ -6,7 +6,7 @@
 import unittest
 
 import frappe
-from frappe.utils import flt, getdate, nowdate
+from frappe.utils import add_days, flt, getdate, nowdate
 
 from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import (
 	create_appointment,
@@ -33,10 +33,12 @@
 		self.assertEqual(plan.status, 'Not Started')
 
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
+		session.start_date = getdate()
 		frappe.get_doc(session).submit()
 		self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'In Progress')
 
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company')
+		session.start_date = add_days(getdate(), 1)
 		frappe.get_doc(session).submit()
 		self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
 
@@ -44,6 +46,7 @@
 		appointment = create_appointment(patient, practitioner, nowdate())
 
 		session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
+		session.start_date = add_days(getdate(), 2)
 		session = frappe.get_doc(session)
 		session.submit()
 		self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
index 6d63f39..b31a952 100644
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe.model.document import Document
-from frappe.utils import flt, today
+from frappe.utils import flt
 
 
 class TherapyPlan(Document):
@@ -63,8 +63,6 @@
 	therapy_session.exercises = therapy_type.exercises
 	therapy_session.appointment = appointment
 
-	if frappe.flags.in_test:
-		therapy_session.start_date = today()
 	return therapy_session.as_dict()
 
 
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index a8f1617..b89b10b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -344,6 +344,7 @@
 	"all": [
 		"erpnext.projects.doctype.project.project.project_status_update_reminder",
 		"erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder",
+		"erpnext.hr.doctype.interview.interview.send_interview_reminder",
 		"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts"
 	],
 	"hourly": [
@@ -388,6 +389,7 @@
 		"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
 		"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
 		"erpnext.non_profit.doctype.membership.membership.set_expired_status"
+		"erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder"
 	],
 	"daily_long": [
 		"erpnext.setup.doctype.email_digest.email_digest.send",
diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js
index 9a3bac0..6b3c29a 100644
--- a/erpnext/hr/doctype/attendance/attendance_list.js
+++ b/erpnext/hr/doctype/attendance/attendance_list.js
@@ -9,83 +9,86 @@
 			return [__(doc.status), "orange", "status,=," + doc.status];
 		}
 	},
+
 	onload: function(list_view) {
 		let me = this;
-		const months = moment.months()
-		list_view.page.add_inner_button( __("Mark Attendance"), function() {
+		const months = moment.months();
+		list_view.page.add_inner_button(__("Mark Attendance"), function() {
 			let dialog = new frappe.ui.Dialog({
 				title: __("Mark Attendance"),
-				fields: [
-					{
-						fieldname: 'employee',
-						label: __('For Employee'),
-						fieldtype: 'Link',
-						options: 'Employee',
-						get_query: () => {
-							return {query: "erpnext.controllers.queries.employee_query"}
-						},
-						reqd: 1,
-						onchange: function() {
-							dialog.set_df_property("unmarked_days", "hidden", 1);
-							dialog.set_df_property("status", "hidden", 1);
-							dialog.set_df_property("month", "value", '');
+				fields: [{
+					fieldname: 'employee',
+					label: __('For Employee'),
+					fieldtype: 'Link',
+					options: 'Employee',
+					get_query: () => {
+						return {query: "erpnext.controllers.queries.employee_query"};
+					},
+					reqd: 1,
+					onchange: function() {
+						dialog.set_df_property("unmarked_days", "hidden", 1);
+						dialog.set_df_property("status", "hidden", 1);
+						dialog.set_df_property("month", "value", '');
+						dialog.set_df_property("unmarked_days", "options", []);
+						dialog.no_unmarked_days_left = false;
+					}
+				},
+				{
+					label: __("For Month"),
+					fieldtype: "Select",
+					fieldname: "month",
+					options: months,
+					reqd: 1,
+					onchange: function() {
+						if (dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
+							dialog.set_df_property("status", "hidden", 0);
 							dialog.set_df_property("unmarked_days", "options", []);
 							dialog.no_unmarked_days_left = false;
+							me.get_multi_select_options(dialog.fields_dict.employee.value, dialog.fields_dict.month.value).then(options => {
+								if (options.length > 0) {
+									dialog.set_df_property("unmarked_days", "hidden", 0);
+									dialog.set_df_property("unmarked_days", "options", options);
+								} else {
+									dialog.no_unmarked_days_left = true;
+								}
+							});
 						}
-					},
-					{
-						label: __("For Month"),
-						fieldtype: "Select",
-						fieldname: "month",
-						options: months,
-						reqd: 1,
-						onchange: function() {
-							if(dialog.fields_dict.employee.value && dialog.fields_dict.month.value) {
-								dialog.set_df_property("status", "hidden", 0);
-								dialog.set_df_property("unmarked_days", "options", []);
-								dialog.no_unmarked_days_left = false;
-								me.get_multi_select_options(dialog.fields_dict.employee.value, dialog.fields_dict.month.value).then(options =>{
-									if (options.length > 0) {
-										dialog.set_df_property("unmarked_days", "hidden", 0);
-										dialog.set_df_property("unmarked_days", "options", options);
-									} else {
-										dialog.no_unmarked_days_left = true;
-									}
-								});
-							}
-						}
-					},
-					{
-						label: __("Status"),
-						fieldtype: "Select",
-						fieldname: "status",
-						options: ["Present", "Absent", "Half Day", "Work From Home"],
-						hidden:1,
-						reqd: 1,
+					}
+				},
+				{
+					label: __("Status"),
+					fieldtype: "Select",
+					fieldname: "status",
+					options: ["Present", "Absent", "Half Day", "Work From Home"],
+					hidden: 1,
+					reqd: 1,
 
-					},
-					{
-						label: __("Unmarked Attendance for days"),
-						fieldname: "unmarked_days",
-						fieldtype: "MultiCheck",
-						options: [],
-						columns: 2,
-						hidden: 1
-					},
-				],
-				primary_action(data)  {
+				},
+				{
+					label: __("Unmarked Attendance for days"),
+					fieldname: "unmarked_days",
+					fieldtype: "MultiCheck",
+					options: [],
+					columns: 2,
+					hidden: 1
+				}],
+				primary_action(data) {
 					if (cur_dialog.no_unmarked_days_left) {
-						frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",[dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
+						frappe.msgprint(__("Attendance for the month of {0} , has already been marked for the Employee {1}",
+							[dialog.fields_dict.month.value, dialog.fields_dict.employee.value]));
 					} else {
-						frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status,data.month]), () => {
+						frappe.confirm(__('Mark attendance as {0} for {1} on selected dates?', [data.status, data.month]), () => {
 							frappe.call({
 								method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance",
 								args: {
 									data: data
 								},
-								callback: function(r) {
+								callback: function (r) {
 									if (r.message === 1) {
-										frappe.show_alert({message: __("Attendance Marked"), indicator: 'blue'});
+										frappe.show_alert({
+											message: __("Attendance Marked"),
+											indicator: 'blue'
+										});
 										cur_dialog.hide();
 									}
 								}
@@ -101,21 +104,26 @@
 			dialog.show();
 		});
 	},
-	get_multi_select_options: function(employee, month){
+
+	get_multi_select_options: function(employee, month) {
 		return new Promise(resolve => {
 			frappe.call({
 				method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days',
 				async: false,
-				args:{
+				args: {
 					employee: employee,
 					month: month,
 				}
 			}).then(r => {
 				var options = [];
-				for(var d in r.message){
+				for (var d in r.message) {
 					var momentObj = moment(r.message[d], 'YYYY-MM-DD');
 					var date = momentObj.format('DD-MM-YYYY');
-					options.push({ "label":date, "value": r.message[d] , "checked": 1});
+					options.push({
+						"label": date,
+						"value": r.message[d],
+						"checked": 1
+					});
 				}
 				resolve(options);
 			});
diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
index eae600d..1e3b9cb 100644
--- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
+++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py
@@ -71,6 +71,7 @@
 	applicant = frappe.new_doc('Job Applicant')
 	applicant.applicant_name = 'Test Researcher'
 	applicant.email_id = 'test@researcher.com'
+	applicant.designation = 'Researcher'
 	applicant.status = 'Open'
 	applicant.cover_letter = 'I am a great Researcher.'
 	applicant.insert()
diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py
index 5cb5bb5..db356bf 100644
--- a/erpnext/hr/doctype/employee_referral/employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/employee_referral.py
@@ -38,8 +38,10 @@
 		status = "Open"
 
 	job_applicant = frappe.new_doc("Job Applicant")
+	job_applicant.source = "Employee Referral"
 	job_applicant.employee_referral = emp_ref.name
 	job_applicant.status = status
+	job_applicant.designation = emp_ref.for_designation
 	job_applicant.applicant_name = emp_ref.full_name
 	job_applicant.email_id = emp_ref.email
 	job_applicant.phone_number = emp_ref.contact_no
diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
index d0ee2fc..1340f62 100644
--- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py
+++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py
@@ -17,6 +17,11 @@
 
 
 class TestEmployeeReferral(unittest.TestCase):
+
+	def setUp(self):
+		frappe.db.sql("DELETE FROM `tabJob Applicant`")
+		frappe.db.sql("DELETE FROM `tabEmployee Referral`")
+
 	def test_workflow_and_status_sync(self):
 		emp_ref = create_employee_referral()
 
@@ -50,6 +55,10 @@
 		add_sal = create_additional_salary(emp_ref)
 		self.assertTrue(add_sal.ref_docname, emp_ref.name)
 
+	def tearDown(self):
+		frappe.db.sql("DELETE FROM `tabJob Applicant`")
+		frappe.db.sql("DELETE FROM `tabEmployee Referral`")
+
 
 def create_employee_referral():
 	emp_ref = frappe.new_doc("Employee Referral")
diff --git a/erpnext/hr/doctype/expected_skill_set/__init__.py b/erpnext/hr/doctype/expected_skill_set/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/__init__.py
diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json
new file mode 100644
index 0000000..899f5bd
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 13:05:06.741330",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "skill",
+  "description"
+ ],
+ "fields": [
+  {
+   "fieldname": "skill",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Skill",
+   "options": "Skill",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "skill.description",
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "in_list_view": 1,
+   "label": "Description"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-12 14:26:33.062549",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Expected Skill Set",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py
new file mode 100644
index 0000000..27120c1
--- /dev/null
+++ b/erpnext/hr/doctype/expected_skill_set/expected_skill_set.py
@@ -0,0 +1,12 @@
+# -*- 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.model.document import Document
+
+
+class ExpectedSkillSet(Document):
+	pass
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 8aa3c0c..4bc066f 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -30,7 +30,13 @@
   "auto_leave_encashment",
   "restrict_backdated_leave_application",
   "hiring_settings",
-  "check_vacancies"
+  "check_vacancies",
+  "send_interview_reminder",
+  "interview_reminder_template",
+  "remind_before",
+  "column_break_29",
+  "send_interview_feedback_reminder",
+  "feedback_reminder_notification_template"
  ],
  "fields": [
   {
@@ -143,6 +149,13 @@
    "label": "Standard Working Hours"
   },
   {
+   "default": "00:15:00",
+   "depends_on": "send_interview_reminder",
+   "fieldname": "remind_before",
+   "fieldtype": "Time",
+   "label": "Remind Before"
+  },
+  {
    "collapsible": 1,
    "fieldname": "reminders_section",
    "fieldtype": "Section Break",
@@ -181,13 +194,45 @@
   {
    "fieldname": "column_break_11",
    "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "send_interview_reminder",
+   "fieldtype": "Check",
+   "label": "Send Interview Reminder"
+  },
+  {
+   "default": "0",
+   "fieldname": "send_interview_feedback_reminder",
+   "fieldtype": "Check",
+   "label": "Send Interview Feedback Reminder"
+  },
+  {
+   "fieldname": "column_break_29",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "send_interview_feedback_reminder",
+   "fieldname": "feedback_reminder_notification_template",
+   "fieldtype": "Link",
+   "label": "Feedback Reminder Notification Template",
+   "mandatory_depends_on": "send_interview_feedback_reminder",
+   "options": "Email Template"
+  },
+  {
+   "depends_on": "send_interview_reminder",
+   "fieldname": "interview_reminder_template",
+   "fieldtype": "Link",
+   "label": "Interview Reminder Notification Template",
+   "mandatory_depends_on": "send_interview_reminder",
+   "options": "Email Template"
   }
  ],
  "icon": "fa fa-cog",
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-08-24 14:54:12.834162",
+ "modified": "2021-09-30 22:42:14.683983",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR Settings",
diff --git a/erpnext/hr/doctype/interview/__init__.py b/erpnext/hr/doctype/interview/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interview/__init__.py
diff --git a/erpnext/hr/doctype/interview/interview.js b/erpnext/hr/doctype/interview/interview.js
new file mode 100644
index 0000000..6341e3a
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.js
@@ -0,0 +1,237 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview', {
+	onload: function (frm) {
+		frm.events.set_job_applicant_query(frm);
+
+		frm.set_query('interviewer', 'interview_details', function () {
+			return {
+				query: 'erpnext.hr.doctype.interview.interview.get_interviewer_list'
+			};
+		});
+	},
+
+	refresh: function (frm) {
+		if (frm.doc.docstatus != 2 && !frm.doc.__islocal) {
+			if (frm.doc.status === 'Pending') {
+				frm.add_custom_button(__('Reschedule Interview'), function() {
+					frm.events.show_reschedule_dialog(frm);
+					frm.refresh();
+				});
+			}
+
+			let allowed_interviewers = [];
+			frm.doc.interview_details.forEach(values => {
+				allowed_interviewers.push(values.interviewer);
+			});
+
+			if ((allowed_interviewers.includes(frappe.session.user))) {
+				frappe.db.get_value('Interview Feedback', {'interviewer': frappe.session.user, 'interview': frm.doc.name, 'docstatus': 1}, 'name', (r) => {
+					if (Object.keys(r).length === 0) {
+						frm.add_custom_button(__('Submit Feedback'), function () {
+							frappe.call({
+								method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set',
+								args: {
+									interview_round: frm.doc.interview_round
+								},
+								callback: function (r) {
+									frm.events.show_feedback_dialog(frm, r.message);
+									frm.refresh();
+								}
+							});
+						}).addClass('btn-primary');
+					}
+				});
+			}
+		}
+	},
+
+	show_reschedule_dialog: function (frm) {
+		let d = new frappe.ui.Dialog({
+			title: 'Reschedule Interview',
+			fields: [
+				{
+					label: 'Schedule On',
+					fieldname: 'scheduled_on',
+					fieldtype: 'Date',
+					reqd: 1
+				},
+				{
+					label: 'From Time',
+					fieldname: 'from_time',
+					fieldtype: 'Time',
+					reqd: 1
+				},
+				{
+					label: 'To Time',
+					fieldname: 'to_time',
+					fieldtype: 'Time',
+					reqd: 1
+				}
+			],
+			primary_action_label: 'Reschedule',
+			primary_action(values) {
+				frm.call({
+					method: 'reschedule_interview',
+					doc: frm.doc,
+					args: {
+						scheduled_on: values.scheduled_on,
+						from_time: values.from_time,
+						to_time: values.to_time
+					}
+				}).then(() => {
+					frm.refresh();
+					d.hide();
+				});
+			}
+		});
+		d.show();
+	},
+
+	show_feedback_dialog: function (frm, data) {
+		let fields = frm.events.get_fields_for_feedback();
+
+		let d = new frappe.ui.Dialog({
+			title: __('Submit Feedback'),
+			fields: [
+				{
+					fieldname: 'skill_set',
+					fieldtype: 'Table',
+					label: __('Skill Assessment'),
+					cannot_add_rows: false,
+					in_editable_grid: true,
+					reqd: 1,
+					fields: fields,
+					data: data
+				},
+				{
+					fieldname: 'result',
+					fieldtype: 'Select',
+					options: ['', 'Cleared', 'Rejected'],
+					label: __('Result')
+				},
+				{
+					fieldname: 'feedback',
+					fieldtype: 'Small Text',
+					label: __('Feedback')
+				}
+			],
+			size: 'large',
+			minimizable: true,
+			primary_action: function(values) {
+				frappe.call({
+					method: 'erpnext.hr.doctype.interview.interview.create_interview_feedback',
+					args: {
+						data: values,
+						interview_name: frm.doc.name,
+						interviewer: frappe.session.user,
+						job_applicant: frm.doc.job_applicant
+					}
+				}).then(() => {
+					frm.refresh();
+				});
+				d.hide();
+			}
+		});
+		d.show();
+	},
+
+	get_fields_for_feedback: function () {
+		return [{
+			fieldtype: 'Link',
+			fieldname: 'skill',
+			options: 'Skill',
+			in_list_view: 1,
+			label: __('Skill')
+		}, {
+			fieldtype: 'Rating',
+			fieldname: 'rating',
+			label: __('Rating'),
+			in_list_view: 1,
+			reqd: 1,
+		}];
+	},
+
+	set_job_applicant_query: function (frm) {
+		frm.set_query('job_applicant', function () {
+			let job_applicant_filters = {
+				status: ['!=', 'Rejected']
+			};
+			if (frm.doc.designation) {
+				job_applicant_filters.designation = frm.doc.designation;
+			}
+			return {
+				filters: job_applicant_filters
+			};
+		});
+	},
+
+	interview_round: async function (frm) {
+		frm.events.reset_values(frm);
+		frm.set_value('job_applicant', '');
+
+		let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message;
+		frm.set_value('designation', round_data.designation);
+		frm.events.set_job_applicant_query(frm);
+
+		if (frm.doc.interview_round) {
+			frm.events.set_interview_details(frm);
+		} else {
+			frm.set_value('interview_details', []);
+		}
+	},
+
+	set_interview_details: function (frm) {
+		frappe.call({
+			method: 'erpnext.hr.doctype.interview.interview.get_interviewers',
+			args: {
+				interview_round: frm.doc.interview_round
+			},
+			callback: function (data) {
+				let interview_details = data.message;
+				frm.set_value('interview_details', []);
+				if (data.message.length) {
+					frm.set_value('interview_details', interview_details);
+				}
+			}
+		});
+	},
+
+	job_applicant: function (frm) {
+		if (!frm.doc.interview_round) {
+			frm.doc.job_applicant = '';
+			frm.refresh();
+			frappe.throw(__('Select Interview Round First'));
+		}
+
+		if (frm.doc.job_applicant) {
+			frm.events.set_designation_and_job_opening(frm);
+		} else {
+			frm.events.reset_values(frm);
+		}
+	},
+
+	set_designation_and_job_opening: async function (frm) {
+		let round_data = (await frappe.db.get_value('Interview Round', frm.doc.interview_round, 'designation')).message;
+		frm.set_value('designation', round_data.designation);
+		frm.events.set_job_applicant_query(frm);
+
+		let job_applicant_data = (await frappe.db.get_value(
+			'Job Applicant', frm.doc.job_applicant, ['designation', 'job_title', 'resume_link'],
+		)).message;
+
+		if (!round_data.designation) {
+			frm.set_value('designation', job_applicant_data.designation);
+		}
+
+		frm.set_value('job_opening', job_applicant_data.job_title);
+		frm.set_value('resume_link', job_applicant_data.resume_link);
+	},
+
+	reset_values: function (frm) {
+		frm.set_value('designation', '');
+		frm.set_value('job_opening', '');
+		frm.set_value('resume_link', '');
+	}
+});
diff --git a/erpnext/hr/doctype/interview/interview.json b/erpnext/hr/doctype/interview/interview.json
new file mode 100644
index 0000000..0d393e7
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.json
@@ -0,0 +1,254 @@
+{
+ "actions": [],
+ "autoname": "HR-INT-.YYYY.-.####",
+ "creation": "2021-04-12 15:03:11.524090",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "interview_details_section",
+  "interview_round",
+  "job_applicant",
+  "job_opening",
+  "designation",
+  "resume_link",
+  "column_break_4",
+  "status",
+  "scheduled_on",
+  "from_time",
+  "to_time",
+  "interview_feedback_section",
+  "interview_details",
+  "ratings_section",
+  "expected_average_rating",
+  "column_break_12",
+  "average_rating",
+  "section_break_13",
+  "interview_summary",
+  "reminded",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "job_applicant",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Job Applicant",
+   "options": "Job Applicant",
+   "reqd": 1
+  },
+  {
+   "fieldname": "job_opening",
+   "fieldtype": "Link",
+   "label": "Job Opening",
+   "options": "Job Opening",
+   "read_only": 1
+  },
+  {
+   "fieldname": "interview_round",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Interview Round",
+   "options": "Interview Round",
+   "reqd": 1
+  },
+  {
+   "default": "Pending",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "options": "Pending\nUnder Review\nCleared\nRejected",
+   "reqd": 1
+  },
+  {
+   "fieldname": "ratings_section",
+   "fieldtype": "Section Break",
+   "label": "Ratings"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "average_rating",
+   "fieldtype": "Rating",
+   "in_list_view": 1,
+   "label": "Obtained Average Rating",
+   "read_only": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "interview_summary",
+   "fieldtype": "Text"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "resume_link",
+   "fieldtype": "Data",
+   "label": "Resume link"
+  },
+  {
+   "fieldname": "interview_details_section",
+   "fieldtype": "Section Break",
+   "label": "Details"
+  },
+  {
+   "fetch_from": "interview_round.expected_average_rating",
+   "fieldname": "expected_average_rating",
+   "fieldtype": "Rating",
+   "label": "Expected Average Rating",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_13",
+   "fieldtype": "Section Break",
+   "label": "Interview Summary"
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "interview_round.designation",
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Designation",
+   "options": "Designation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Interview",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "scheduled_on",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Scheduled On",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "reminded",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Reminded"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "interview_details",
+   "fieldtype": "Table",
+   "options": "Interview Detail"
+  },
+  {
+   "fieldname": "interview_feedback_section",
+   "fieldtype": "Section Break",
+   "label": "Feedback"
+  },
+  {
+   "fieldname": "from_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "From Time",
+   "reqd": 1,
+   "set_only_once": 1
+  },
+  {
+   "fieldname": "to_time",
+   "fieldtype": "Time",
+   "in_list_view": 1,
+   "label": "To Time",
+   "reqd": 1,
+   "set_only_once": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [
+  {
+   "link_doctype": "Interview Feedback",
+   "link_fieldname": "interview"
+  }
+ ],
+ "modified": "2021-09-30 13:30:05.421035",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Interviewer",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "job_applicant",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview/interview.py b/erpnext/hr/doctype/interview/interview.py
new file mode 100644
index 0000000..955acca
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview.py
@@ -0,0 +1,293 @@
+# -*- 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 datetime
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import cstr, get_datetime, get_link_to_form
+
+
+class DuplicateInterviewRoundError(frappe.ValidationError):
+	pass
+
+class Interview(Document):
+	def validate(self):
+		self.validate_duplicate_interview()
+		self.validate_designation()
+		self.validate_overlap()
+
+	def on_submit(self):
+		if self.status not in ['Cleared', 'Rejected']:
+			frappe.throw(_('Only Interviews with Cleared or Rejected status can be submitted.'), title=_('Not Allowed'))
+
+	def validate_duplicate_interview(self):
+		duplicate_interview = frappe.db.exists('Interview', {
+				'job_applicant': self.job_applicant,
+				'interview_round': self.interview_round,
+				'docstatus': 1
+			}
+		)
+
+		if duplicate_interview:
+			frappe.throw(_('Job Applicants are not allowed to appear twice for the same Interview round. Interview {0} already scheduled for Job Applicant {1}').format(
+				frappe.bold(get_link_to_form('Interview', duplicate_interview)),
+				frappe.bold(self.job_applicant)
+			))
+
+	def validate_designation(self):
+		applicant_designation = frappe.db.get_value('Job Applicant', self.job_applicant, 'designation')
+		if self.designation :
+			if self.designation != applicant_designation:
+				frappe.throw(_('Interview Round {0} is only for Designation {1}. Job Applicant has applied for the role {2}').format(
+					self.interview_round, frappe.bold(self.designation), applicant_designation),
+					exc=DuplicateInterviewRoundError)
+		else:
+			self.designation = applicant_designation
+
+	def validate_overlap(self):
+		interviewers = [entry.interviewer for entry in self.interview_details] or ['']
+
+		overlaps = frappe.db.sql("""
+			SELECT interview.name
+			FROM `tabInterview` as interview
+			INNER JOIN `tabInterview Detail` as detail
+			WHERE
+				interview.scheduled_on = %s and interview.name != %s and interview.docstatus != 2
+				and (interview.job_applicant = %s or detail.interviewer IN %s) and
+				((from_time < %s and to_time > %s) or
+				(from_time > %s and to_time < %s) or
+				(from_time = %s))
+			""", (self.scheduled_on, self.name, self.job_applicant, interviewers,
+			self.from_time, self.to_time, self.from_time, self.to_time, self.from_time))
+
+		if overlaps:
+			overlapping_details = _('Interview overlaps with {0}').format(get_link_to_form('Interview', overlaps[0][0]))
+			frappe.throw(overlapping_details, title=_('Overlap'))
+
+
+	@frappe.whitelist()
+	def reschedule_interview(self, scheduled_on, from_time, to_time):
+		original_date = self.scheduled_on
+		from_time = self.from_time
+		to_time = self.to_time
+
+		self.db_set({
+			'scheduled_on': scheduled_on,
+			'from_time': from_time,
+			'to_time': to_time
+		})
+		self.notify_update()
+
+		recipients = get_recipients(self.name)
+
+		try:
+			frappe.sendmail(
+				recipients= recipients,
+				subject=_('Interview: {0} Rescheduled').format(self.name),
+				message=_('Your Interview session is rescheduled from {0} {1} - {2} to {3} {4} - {5}').format(
+					original_date, from_time, to_time, self.scheduled_on, self.from_time, self.to_time),
+				reference_doctype=self.doctype,
+				reference_name=self.name
+			)
+		except Exception:
+			frappe.msgprint(_('Failed to send the Interview Reschedule notification. Please configure your email account.'))
+
+		frappe.msgprint(_('Interview Rescheduled successfully'), indicator='green')
+
+
+def get_recipients(name, for_feedback=0):
+	interview = frappe.get_doc('Interview', name)
+
+	if for_feedback:
+		recipients = [d.interviewer for d in interview.interview_details if not d.interview_feedback]
+	else:
+		recipients = [d.interviewer for d in interview.interview_details]
+		recipients.append(frappe.db.get_value('Job Applicant', interview.job_applicant, 'email_id'))
+
+	return recipients
+
+
+@frappe.whitelist()
+def get_interviewers(interview_round):
+	return frappe.get_all('Interviewer', filters={'parent': interview_round}, fields=['user as interviewer'])
+
+
+def send_interview_reminder():
+	reminder_settings = frappe.db.get_value('HR Settings', 'HR Settings',
+		['send_interview_reminder', 'interview_reminder_template'], as_dict=True)
+
+	if not reminder_settings.send_interview_reminder:
+		return
+
+	remind_before = cstr(frappe.db.get_single_value('HR Settings', 'remind_before')) or '01:00:00'
+	remind_before = datetime.datetime.strptime(remind_before, '%H:%M:%S')
+	reminder_date_time = datetime.datetime.now() + datetime.timedelta(
+		hours=remind_before.hour, minutes=remind_before.minute, seconds=remind_before.second)
+
+	interviews = frappe.get_all('Interview', filters={
+		'scheduled_on': ['between', (datetime.datetime.now(), reminder_date_time)],
+		'status': 'Pending',
+		'reminded': 0,
+		'docstatus': ['!=', 2]
+	})
+
+	interview_template = frappe.get_doc('Email Template', reminder_settings.interview_reminder_template)
+
+	for d in interviews:
+		doc = frappe.get_doc('Interview', d.name)
+		context = doc.as_dict()
+		message = frappe.render_template(interview_template.response, context)
+		recipients = get_recipients(doc.name)
+
+		frappe.sendmail(
+			recipients= recipients,
+			subject=interview_template.subject,
+			message=message,
+			reference_doctype=doc.doctype,
+			reference_name=doc.name
+		)
+
+		doc.db_set('reminded', 1)
+
+
+def send_daily_feedback_reminder():
+	reminder_settings = frappe.db.get_value('HR Settings', 'HR Settings',
+		['send_interview_feedback_reminder', 'feedback_reminder_notification_template'], as_dict=True)
+
+	if not reminder_settings.send_interview_feedback_reminder:
+		return
+
+	interview_feedback_template = frappe.get_doc('Email Template', reminder_settings.feedback_reminder_notification_template)
+	interviews = frappe.get_all('Interview', filters={'status': ['in', ['Under Review', 'Pending']], 'docstatus': ['!=', 2]})
+
+	for entry in interviews:
+		recipients = get_recipients(entry.name, for_feedback=1)
+
+		doc = frappe.get_doc('Interview', entry.name)
+		context = doc.as_dict()
+
+		message = frappe.render_template(interview_feedback_template.response, context)
+
+		if len(recipients):
+			frappe.sendmail(
+				recipients= recipients,
+				subject=interview_feedback_template.subject,
+				message=message,
+				reference_doctype='Interview',
+				reference_name=entry.name
+			)
+
+
+@frappe.whitelist()
+def get_expected_skill_set(interview_round):
+	return frappe.get_all('Expected Skill Set', filters ={'parent': interview_round}, fields=['skill'])
+
+
+@frappe.whitelist()
+def create_interview_feedback(data, interview_name, interviewer, job_applicant):
+	import json
+
+	from six import string_types
+
+	if isinstance(data, string_types):
+		data = frappe._dict(json.loads(data))
+
+	if frappe.session.user != interviewer:
+		frappe.throw(_('Only Interviewer Are allowed to submit Interview Feedback'))
+
+	interview_feedback = frappe.new_doc('Interview Feedback')
+	interview_feedback.interview = interview_name
+	interview_feedback.interviewer = interviewer
+	interview_feedback.job_applicant = job_applicant
+
+	for d in data.skill_set:
+		d = frappe._dict(d)
+		interview_feedback.append('skill_assessment', {'skill': d.skill, 'rating': d.rating})
+
+	interview_feedback.feedback = data.feedback
+	interview_feedback.result = data.result
+
+	interview_feedback.save()
+	interview_feedback.submit()
+
+	frappe.msgprint(_('Interview Feedback {0} submitted successfully').format(
+		get_link_to_form('Interview Feedback', interview_feedback.name)))
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_interviewer_list(doctype, txt, searchfield, start, page_len, filters):
+	filters = [
+		['Has Role', 'parent', 'like', '%{}%'.format(txt)],
+		['Has Role', 'role', '=', 'interviewer'],
+		['Has Role', 'parenttype', '=', 'User']
+	]
+
+	if filters and isinstance(filters, list):
+		filters.extend(filters)
+
+	return frappe.get_all('Has Role', limit_start=start, limit_page_length=page_len,
+		filters=filters, fields = ['parent'], as_list=1)
+
+
+@frappe.whitelist()
+def get_events(start, end, filters=None):
+	"""Returns events for Gantt / Calendar view rendering.
+
+	:param start: Start date-time.
+	:param end: End date-time.
+	:param filters: Filters (JSON).
+	"""
+	from frappe.desk.calendar import get_event_conditions
+
+	events = []
+
+	event_color = {
+		"Pending": "#fff4f0",
+		"Under Review": "#d3e8fc",
+		"Cleared": "#eaf5ed",
+		"Rejected": "#fce7e7"
+	}
+
+	conditions = get_event_conditions('Interview', filters)
+
+	interviews = frappe.db.sql("""
+			SELECT DISTINCT
+				`tabInterview`.name, `tabInterview`.job_applicant, `tabInterview`.interview_round,
+				`tabInterview`.scheduled_on, `tabInterview`.status, `tabInterview`.from_time as from_time,
+				`tabInterview`.to_time as to_time
+			from
+				`tabInterview`
+			where
+				(`tabInterview`.scheduled_on between %(start)s and %(end)s)
+				and docstatus != 2
+				{conditions}
+			""".format(conditions=conditions), {
+				"start": start,
+				"end": end
+			}, as_dict=True, update={"allDay": 0})
+
+	for d in interviews:
+		subject_data = []
+		for field in ["name", "job_applicant", "interview_round"]:
+			if not d.get(field):
+				continue
+			subject_data.append(d.get(field))
+
+		color = event_color.get(d.status)
+		interview_data = {
+			'from': get_datetime('%s %s' % (d.scheduled_on, d.from_time or '00:00:00')),
+			'to': get_datetime('%s %s' % (d.scheduled_on, d.to_time or '00:00:00')),
+			'name': d.name,
+			'subject': '\n'.join(subject_data),
+			'color': color if color else "#89bcde"
+		}
+
+		events.append(interview_data)
+
+	return events
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview/interview_calendar.js b/erpnext/hr/doctype/interview/interview_calendar.js
new file mode 100644
index 0000000..b46b72e
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_calendar.js
@@ -0,0 +1,14 @@
+
+frappe.views.calendar['Interview'] = {
+	field_map: {
+		'start': 'from',
+		'end': 'to',
+		'id': 'name',
+		'title': 'subject',
+		'allDay': 'allDay',
+		'color': 'color'
+	},
+	order_by: 'scheduled_on',
+	gantt: true,
+	get_events_method: 'erpnext.hr.doctype.interview.interview.get_events'
+};
diff --git a/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html b/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html
new file mode 100644
index 0000000..8d39fb5
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_feedback_reminder_template.html
@@ -0,0 +1,5 @@
+<h1>Interview Feedback Reminder</h1>
+
+<p>
+	Interview Feedback for Interview {{ name }} is not submitted yet. Please submit your feedback. Thank you, good day!
+</p>
diff --git a/erpnext/hr/doctype/interview/interview_list.js b/erpnext/hr/doctype/interview/interview_list.js
new file mode 100644
index 0000000..b1f072f
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings['Interview'] = {
+	has_indicator_for_draft: 1,
+	get_indicator: function(doc) {
+		let status_color = {
+			'Pending': 'orange',
+			'Under Review': 'blue',
+			'Cleared': 'green',
+			'Rejected': 'red',
+		};
+		return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status];
+	}
+};
diff --git a/erpnext/hr/doctype/interview/interview_reminder_notification_template.html b/erpnext/hr/doctype/interview/interview_reminder_notification_template.html
new file mode 100644
index 0000000..76de46e
--- /dev/null
+++ b/erpnext/hr/doctype/interview/interview_reminder_notification_template.html
@@ -0,0 +1,5 @@
+<h1>Interview Reminder</h1>
+
+<p>
+	Interview: {{name}} is scheduled on {{scheduled_on}} from {{from_time}} to {{to_time}}
+</p>
diff --git a/erpnext/hr/doctype/interview/test_interview.py b/erpnext/hr/doctype/interview/test_interview.py
new file mode 100644
index 0000000..4612e17
--- /dev/null
+++ b/erpnext/hr/doctype/interview/test_interview.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import datetime
+import os
+import unittest
+
+import frappe
+from frappe import _
+from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.utils import add_days, getdate, nowtime
+
+from erpnext.hr.doctype.designation.test_designation import create_designation
+from erpnext.hr.doctype.interview.interview import DuplicateInterviewRoundError
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+
+
+class TestInterview(unittest.TestCase):
+	def test_validations_for_designation(self):
+		job_applicant = create_job_applicant()
+		interview = create_interview_and_dependencies(job_applicant.name, designation='_Test_Sales_manager', save=0)
+		self.assertRaises(DuplicateInterviewRoundError, interview.save)
+
+	def test_notification_on_rescheduling(self):
+		job_applicant = create_job_applicant()
+		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -4))
+
+		previous_scheduled_date = interview.scheduled_on
+		frappe.db.sql("DELETE FROM `tabEmail Queue`")
+
+		interview.reschedule_interview(add_days(getdate(previous_scheduled_date), 2),
+			from_time=nowtime(), to_time=nowtime())
+		interview.reload()
+
+		self.assertEqual(interview.scheduled_on, add_days(getdate(previous_scheduled_date), 2))
+
+		notification = frappe.get_all("Email Queue", filters={"message": ("like", "%Your Interview session is rescheduled from%")})
+		self.assertIsNotNone(notification)
+
+	def test_notification_for_scheduling(self):
+		from erpnext.hr.doctype.interview.interview import send_interview_reminder
+
+		setup_reminder_settings()
+
+		job_applicant = create_job_applicant()
+		scheduled_on = datetime.datetime.now() + datetime.timedelta(minutes=10)
+
+		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on)
+
+		frappe.db.sql("DELETE FROM `tabEmail Queue`")
+		send_interview_reminder()
+
+		interview.reload()
+
+		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+		self.assertTrue("Subject: Interview Reminder" in email_queue[0].message)
+
+	def test_notification_for_feedback_submission(self):
+		from erpnext.hr.doctype.interview.interview import send_daily_feedback_reminder
+
+		setup_reminder_settings()
+
+		job_applicant = create_job_applicant()
+		scheduled_on = add_days(getdate(), -4)
+		create_interview_and_dependencies(job_applicant.name, scheduled_on=scheduled_on)
+
+		frappe.db.sql("DELETE FROM `tabEmail Queue`")
+		send_daily_feedback_reminder()
+
+		email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
+		self.assertTrue("Subject: Interview Feedback Reminder" in email_queue[0].message)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+
+def create_interview_and_dependencies(job_applicant, scheduled_on=None, from_time=None, to_time=None, designation=None, save=1):
+	if designation:
+		designation=create_designation(designation_name = "_Test_Sales_manager").name
+
+	interviewer_1 = create_user("test_interviewer1@example.com", "Interviewer")
+	interviewer_2 = create_user("test_interviewer2@example.com", "Interviewer")
+
+	interview_round = create_interview_round(
+		"Technical Round", ["Python", "JS"],
+		designation=designation, save=True
+	)
+
+	interview = frappe.new_doc("Interview")
+	interview.interview_round = interview_round.name
+	interview.job_applicant = job_applicant
+	interview.scheduled_on = scheduled_on or getdate()
+	interview.from_time = from_time or nowtime()
+	interview.to_time = to_time or nowtime()
+
+	interview.append("interview_details", {"interviewer": interviewer_1.name})
+	interview.append("interview_details", {"interviewer": interviewer_2.name})
+
+	if save:
+		interview.save()
+
+	return interview
+
+def create_interview_round(name, skill_set, interviewers=[], designation=None, save=True):
+	create_skill_set(skill_set)
+	interview_round = frappe.new_doc("Interview Round")
+	interview_round.round_name = name
+	interview_round.interview_type = create_interview_type()
+	interview_round.expected_average_rating = 4
+	if designation:
+		interview_round.designation = designation
+
+	for skill in skill_set:
+		interview_round.append("expected_skill_set", {"skill": skill})
+
+	for interviewer in interviewers:
+		interview_round.append("interviewer", {
+			"user": interviewer
+		})
+
+	if save:
+		interview_round.save()
+
+	return interview_round
+
+def create_skill_set(skill_set):
+	for skill in skill_set:
+		if not frappe.db.exists("Skill", skill):
+			doc = frappe.new_doc("Skill")
+			doc.skill_name = skill
+			doc.save()
+
+def create_interview_type(name="test_interview_type"):
+	if frappe.db.exists("Interview Type", name):
+		return frappe.get_doc("Interview Type", name).name
+	else:
+		doc = frappe.new_doc("Interview Type")
+		doc.name = name
+		doc.description = "_Test_Description"
+		doc.save()
+
+		return doc.name
+
+def setup_reminder_settings():
+	if not frappe.db.exists('Email Template', _('Interview Reminder')):
+		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
+		response = frappe.read_file(os.path.join(base_path, 'interview/interview_reminder_notification_template.html'))
+
+		frappe.get_doc({
+			'doctype': 'Email Template',
+			'name': _('Interview Reminder'),
+			'response': response,
+			'subject': _('Interview Reminder'),
+			'owner': frappe.session.user,
+		}).insert(ignore_permissions=True)
+
+	if not frappe.db.exists('Email Template', _('Interview Feedback Reminder')):
+		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
+		response = frappe.read_file(os.path.join(base_path, 'interview/interview_feedback_reminder_template.html'))
+
+		frappe.get_doc({
+			'doctype': 'Email Template',
+			'name': _('Interview Feedback Reminder'),
+			'response': response,
+			'subject': _('Interview Feedback Reminder'),
+			'owner': frappe.session.user,
+		}).insert(ignore_permissions=True)
+
+	hr_settings = frappe.get_doc('HR Settings')
+	hr_settings.interview_reminder_template = _('Interview Reminder')
+	hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder')
+	hr_settings.save()
diff --git a/erpnext/hr/doctype/interview_detail/__init__.py b/erpnext/hr/doctype/interview_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/__init__.py
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.js b/erpnext/hr/doctype/interview_detail/interview_detail.js
new file mode 100644
index 0000000..88518ca
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Detail', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.json b/erpnext/hr/doctype/interview_detail/interview_detail.json
new file mode 100644
index 0000000..b5b49c0
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.json
@@ -0,0 +1,74 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 16:24:10.382863",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "interviewer",
+  "interview_feedback",
+  "average_rating",
+  "result",
+  "column_break_4",
+  "comments"
+ ],
+ "fields": [
+  {
+   "fieldname": "interviewer",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Interviewer",
+   "options": "User"
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "interview_feedback",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Interview Feedback",
+   "options": "Interview Feedback",
+   "read_only": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "average_rating",
+   "fieldtype": "Rating",
+   "in_list_view": 1,
+   "label": "Average Rating",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "allow_on_submit": 1,
+   "fetch_from": "interview_feedback.feedback",
+   "fieldname": "comments",
+   "fieldtype": "Text",
+   "label": "Comments",
+   "read_only": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "result",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Result",
+   "options": "\nCleared\nRejected",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-29 13:13:25.865063",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_detail/interview_detail.py b/erpnext/hr/doctype/interview_detail/interview_detail.py
new file mode 100644
index 0000000..8be3d34
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/interview_detail.py
@@ -0,0 +1,12 @@
+# -*- 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.model.document import Document
+
+
+class InterviewDetail(Document):
+	pass
diff --git a/erpnext/hr/doctype/interview_detail/test_interview_detail.py b/erpnext/hr/doctype/interview_detail/test_interview_detail.py
new file mode 100644
index 0000000..a29dfff
--- /dev/null
+++ b/erpnext/hr/doctype/interview_detail/test_interview_detail.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestInterviewDetail(unittest.TestCase):
+	pass
diff --git a/erpnext/hr/doctype/interview_feedback/__init__.py b/erpnext/hr/doctype/interview_feedback/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/__init__.py
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.js b/erpnext/hr/doctype/interview_feedback/interview_feedback.js
new file mode 100644
index 0000000..dec559f
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Feedback', {
+	onload: function(frm) {
+		frm.ignore_doctypes_on_cancel_all = ['Interview'];
+
+		frm.set_query('interview', function() {
+			return {
+				filters: {
+					docstatus: ['!=', 2]
+				}
+			};
+		});
+	},
+
+	interview_round: function(frm) {
+		frappe.call({
+			method: 'erpnext.hr.doctype.interview.interview.get_expected_skill_set',
+			args: {
+				interview_round: frm.doc.interview_round
+			},
+			callback: function(r) {
+				frm.set_value('skill_assessment', r.message);
+			}
+		});
+	},
+
+	interview: function(frm) {
+		frappe.call({
+			method: 'erpnext.hr.doctype.interview_feedback.interview_feedback.get_applicable_interviewers',
+			args: {
+				interview: frm.doc.interview || ''
+			},
+			callback: function(r) {
+				frm.set_query('interviewer', function() {
+					return {
+						filters: {
+							name: ['in', r.message]
+						}
+					};
+				});
+			}
+		});
+
+	},
+
+	interviewer: function(frm) {
+		if (!frm.doc.interview) {
+			frappe.throw(__('Select Interview first'));
+			frm.set_value('interviewer', '');
+		}
+	}
+});
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.json b/erpnext/hr/doctype/interview_feedback/interview_feedback.json
new file mode 100644
index 0000000..6a2f7e8
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.json
@@ -0,0 +1,171 @@
+{
+ "actions": [],
+ "autoname": "HR-INT-FEED-.####",
+ "creation": "2021-04-12 17:03:13.833285",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "details_section",
+  "interview",
+  "interview_round",
+  "job_applicant",
+  "column_break_3",
+  "interviewer",
+  "result",
+  "section_break_4",
+  "skill_assessment",
+  "average_rating",
+  "section_break_7",
+  "feedback",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "interview",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Interview",
+   "options": "Interview",
+   "reqd": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fetch_from": "interview.interview_round",
+   "fieldname": "interview_round",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Interview Round",
+   "options": "Interview Round",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "interviewer",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Interviewer",
+   "options": "User",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Skill Assessment"
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "skill_assessment",
+   "fieldtype": "Table",
+   "options": "Skill Assessment",
+   "reqd": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "average_rating",
+   "fieldtype": "Rating",
+   "in_list_view": 1,
+   "label": "Average Rating",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_7",
+   "fieldtype": "Section Break",
+   "label": "Feedback"
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Interview Feedback",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "feedback",
+   "fieldtype": "Text"
+  },
+  {
+   "fieldname": "result",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Result",
+   "options": "\nCleared\nRejected",
+   "reqd": 1
+  },
+  {
+   "fieldname": "details_section",
+   "fieldtype": "Section Break",
+   "label": "Details"
+  },
+  {
+   "fetch_from": "interview.job_applicant",
+   "fieldname": "job_applicant",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Job Applicant",
+   "options": "Job Applicant",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-09-30 13:30:49.955352",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Feedback",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Interviewer",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "interviewer",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_feedback/interview_feedback.py b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
new file mode 100644
index 0000000..1c5a494
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/interview_feedback.py
@@ -0,0 +1,88 @@
+# -*- 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.model.document import Document
+from frappe.utils import flt, get_link_to_form, getdate
+
+
+class InterviewFeedback(Document):
+	def validate(self):
+		self.validate_interviewer()
+		self.validate_interview_date()
+		self.validate_duplicate()
+		self.calculate_average_rating()
+
+	def on_submit(self):
+		self.update_interview_details()
+
+	def on_cancel(self):
+		self.update_interview_details()
+
+	def validate_interviewer(self):
+		applicable_interviewers = get_applicable_interviewers(self.interview)
+		if self.interviewer not in applicable_interviewers:
+			frappe.throw(_('{0} is not allowed to submit Interview Feedback for the Interview: {1}').format(
+				frappe.bold(self.interviewer), frappe.bold(self.interview)))
+
+	def validate_interview_date(self):
+		scheduled_date = frappe.db.get_value('Interview', self.interview, 'scheduled_on')
+
+		if getdate() < getdate(scheduled_date) and self.docstatus == 1:
+			frappe.throw(_('{0} submission before {1} is not allowed').format(
+				frappe.bold('Interview Feedback'),
+				frappe.bold('Interview Scheduled Date')
+			))
+
+	def validate_duplicate(self):
+		duplicate_feedback = frappe.db.exists('Interview Feedback', {
+			'interviewer': self.interviewer,
+			'interview': self.interview,
+			'docstatus': 1
+		})
+
+		if duplicate_feedback:
+			frappe.throw(_('Feedback already submitted for the Interview {0}. Please cancel the previous Interview Feedback {1} to continue.').format(
+				self.interview, get_link_to_form('Interview Feedback', duplicate_feedback)))
+
+	def calculate_average_rating(self):
+		total_rating = 0
+		for d in self.skill_assessment:
+			if d.rating:
+				total_rating += d.rating
+
+		self.average_rating = flt(total_rating / len(self.skill_assessment) if len(self.skill_assessment) else 0)
+
+	def update_interview_details(self):
+		doc = frappe.get_doc('Interview', self.interview)
+		total_rating = 0
+
+		if self.docstatus == 2:
+			for entry in doc.interview_details:
+				if entry.interview_feedback == self.name:
+					entry.average_rating = entry.interview_feedback = entry.comments = entry.result = None
+					break
+		else:
+			for entry in doc.interview_details:
+				if entry.interviewer == self.interviewer:
+					entry.average_rating = self.average_rating
+					entry.interview_feedback = self.name
+					entry.comments = self.feedback
+					entry.result = self.result
+
+				if entry.average_rating:
+					total_rating += entry.average_rating
+
+		doc.average_rating = flt(total_rating / len(doc.interview_details) if len(doc.interview_details) else 0)
+		doc.save()
+		doc.notify_update()
+
+
+@frappe.whitelist()
+def get_applicable_interviewers(interview):
+	data = frappe.get_all('Interview Detail', filters={'parent': interview}, fields=['interviewer'])
+	return [d.interviewer for d in data]
diff --git a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
new file mode 100644
index 0000000..c4b7981
--- /dev/null
+++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+import frappe
+from frappe.utils import add_days, flt, getdate
+
+from erpnext.hr.doctype.interview.test_interview import (
+	create_interview_and_dependencies,
+	create_skill_set,
+)
+from erpnext.hr.doctype.job_applicant.test_job_applicant import create_job_applicant
+
+
+class TestInterviewFeedback(unittest.TestCase):
+	def test_validation_for_skill_set(self):
+		frappe.set_user("Administrator")
+		job_applicant = create_job_applicant()
+		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -1))
+		skill_ratings = get_skills_rating(interview.interview_round)
+
+		interviewer = interview.interview_details[0].interviewer
+		create_skill_set(['Leadership'])
+
+		interview_feedback = create_interview_feedback(interview.name, interviewer, skill_ratings)
+		interview_feedback.append("skill_assessment", {"skill": 'Leadership', 'rating': 4})
+		frappe.set_user(interviewer)
+
+		self.assertRaises(frappe.ValidationError, interview_feedback.save)
+
+		frappe.set_user("Administrator")
+
+	def test_average_ratings_on_feedback_submission_and_cancellation(self):
+		job_applicant = create_job_applicant()
+		interview = create_interview_and_dependencies(job_applicant.name, scheduled_on=add_days(getdate(), -1))
+		skill_ratings = get_skills_rating(interview.interview_round)
+
+		# For First Interviewer Feedback
+		interviewer = interview.interview_details[0].interviewer
+		frappe.set_user(interviewer)
+
+		# calculating Average
+		feedback_1 = create_interview_feedback(interview.name, interviewer, skill_ratings)
+
+		total_rating = 0
+		for d in feedback_1.skill_assessment:
+			if d.rating:
+				total_rating += d.rating
+
+		avg_rating = flt(total_rating / len(feedback_1.skill_assessment) if len(feedback_1.skill_assessment) else 0)
+
+		self.assertEqual(flt(avg_rating, 3), feedback_1.average_rating)
+
+		avg_on_interview_detail = frappe.db.get_value('Interview Detail', {
+			'parent': feedback_1.interview,
+			'interviewer': feedback_1.interviewer,
+			'interview_feedback': feedback_1.name
+		}, 'average_rating')
+
+		# 1. average should be reflected in Interview Detail.
+		self.assertEqual(avg_on_interview_detail, round(feedback_1.average_rating))
+
+		'''For Second Interviewer Feedback'''
+		interviewer = interview.interview_details[1].interviewer
+		frappe.set_user(interviewer)
+
+		feedback_2 = create_interview_feedback(interview.name, interviewer, skill_ratings)
+		interview.reload()
+
+		feedback_2.cancel()
+		interview.reload()
+
+		frappe.set_user("Administrator")
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+
+def create_interview_feedback(interview, interviewer, skills_ratings):
+	interview_feedback = frappe.new_doc("Interview Feedback")
+	interview_feedback.interview = interview
+	interview_feedback.interviewer = interviewer
+	interview_feedback.result = "Cleared"
+
+	for rating in skills_ratings:
+		interview_feedback.append("skill_assessment", rating)
+
+	interview_feedback.save()
+	interview_feedback.submit()
+
+	return interview_feedback
+
+
+def get_skills_rating(interview_round):
+	import random
+
+	skills = frappe.get_all("Expected Skill Set", filters={"parent": interview_round}, fields = ["skill"])
+	for d in skills:
+		d["rating"] = random.randint(1, 5)
+	return skills
diff --git a/erpnext/hr/doctype/interview_round/__init__.py b/erpnext/hr/doctype/interview_round/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/__init__.py
diff --git a/erpnext/hr/doctype/interview_round/interview_round.js b/erpnext/hr/doctype/interview_round/interview_round.js
new file mode 100644
index 0000000..6a608b0
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Interview Round", {
+	refresh: function(frm) {
+		if (!frm.doc.__islocal) {
+			frm.add_custom_button(__("Create Interview"), function() {
+				frm.events.create_interview(frm);
+			});
+		}
+	},
+	create_interview: function(frm) {
+		frappe.call({
+			method: "erpnext.hr.doctype.interview_round.interview_round.create_interview",
+			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/interview_round/interview_round.json b/erpnext/hr/doctype/interview_round/interview_round.json
new file mode 100644
index 0000000..9c95185
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.json
@@ -0,0 +1,118 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:round_name",
+ "creation": "2021-04-12 12:57:19.902866",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "round_name",
+  "interview_type",
+  "interviewers",
+  "column_break_3",
+  "designation",
+  "expected_average_rating",
+  "expected_skills_section",
+  "expected_skill_set"
+ ],
+ "fields": [
+  {
+   "fieldname": "round_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Round Name",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation"
+  },
+  {
+   "fieldname": "expected_skills_section",
+   "fieldtype": "Section Break",
+   "label": "Expected Skillset"
+  },
+  {
+   "fieldname": "expected_skill_set",
+   "fieldtype": "Table",
+   "options": "Expected Skill Set",
+   "reqd": 1
+  },
+  {
+   "fieldname": "expected_average_rating",
+   "fieldtype": "Rating",
+   "label": "Expected Average Rating",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "interview_type",
+   "fieldtype": "Link",
+   "label": "Interview Type",
+   "options": "Interview Type",
+   "reqd": 1
+  },
+  {
+   "fieldname": "interviewers",
+   "fieldtype": "Table MultiSelect",
+   "label": "Interviewers",
+   "options": "Interviewer"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-09-30 13:01:25.666660",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Round",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Interviewer",
+   "select": 1,
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_round/interview_round.py b/erpnext/hr/doctype/interview_round/interview_round.py
new file mode 100644
index 0000000..8230c78
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/interview_round.py
@@ -0,0 +1,35 @@
+# -*- 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 json
+
+import frappe
+from frappe.model.document import Document
+
+
+class InterviewRound(Document):
+	pass
+
+@frappe.whitelist()
+def create_interview(doc):
+	if isinstance(doc, str):
+		doc = json.loads(doc)
+		doc = frappe.get_doc(doc)
+
+	interview = frappe.new_doc("Interview")
+	interview.interview_round = doc.name
+	interview.designation = doc.designation
+
+	if doc.interviewers:
+		interview.interview_details = []
+		for data in doc.interviewers:
+			interview.append("interview_details", {
+				"interviewer": data.user
+			})
+	return interview
+
+
+
diff --git a/erpnext/hr/doctype/interview_round/test_interview_round.py b/erpnext/hr/doctype/interview_round/test_interview_round.py
new file mode 100644
index 0000000..932d3de
--- /dev/null
+++ b/erpnext/hr/doctype/interview_round/test_interview_round.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+# import frappe
+
+
+class TestInterviewRound(unittest.TestCase):
+	pass
+
diff --git a/erpnext/hr/doctype/interview_type/__init__.py b/erpnext/hr/doctype/interview_type/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/__init__.py
diff --git a/erpnext/hr/doctype/interview_type/interview_type.js b/erpnext/hr/doctype/interview_type/interview_type.js
new file mode 100644
index 0000000..af77b52
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Interview Type', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/hr/doctype/interview_type/interview_type.json b/erpnext/hr/doctype/interview_type/interview_type.json
new file mode 100644
index 0000000..14636a1
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.json
@@ -0,0 +1,73 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2021-04-12 14:44:40.664034",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "description"
+ ],
+ "fields": [
+  {
+   "fieldname": "description",
+   "fieldtype": "Text",
+   "in_list_view": 1,
+   "label": "Description"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [
+  {
+   "link_doctype": "Interview Round",
+   "link_fieldname": "interview_type"
+  }
+ ],
+ "modified": "2021-09-30 13:00:16.471518",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interview Type",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interview_type/interview_type.py b/erpnext/hr/doctype/interview_type/interview_type.py
new file mode 100644
index 0000000..ee5be54
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/interview_type.py
@@ -0,0 +1,12 @@
+# -*- 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.model.document import Document
+
+
+class InterviewType(Document):
+	pass
diff --git a/erpnext/hr/doctype/interview_type/test_interview_type.py b/erpnext/hr/doctype/interview_type/test_interview_type.py
new file mode 100644
index 0000000..a5d3cf9
--- /dev/null
+++ b/erpnext/hr/doctype/interview_type/test_interview_type.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestInterviewType(unittest.TestCase):
+	pass
diff --git a/erpnext/hr/doctype/interviewer/__init__.py b/erpnext/hr/doctype/interviewer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/__init__.py
diff --git a/erpnext/hr/doctype/interviewer/interviewer.json b/erpnext/hr/doctype/interviewer/interviewer.json
new file mode 100644
index 0000000..a37b8b0
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/interviewer.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 17:38:19.354734",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "user"
+ ],
+ "fields": [
+  {
+   "fieldname": "user",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "User",
+   "options": "User"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-13 13:41:35.817568",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Interviewer",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/interviewer/interviewer.py b/erpnext/hr/doctype/interviewer/interviewer.py
new file mode 100644
index 0000000..1c8dbbe
--- /dev/null
+++ b/erpnext/hr/doctype/interviewer/interviewer.py
@@ -0,0 +1,12 @@
+# -*- 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.model.document import Document
+
+
+class Interviewer(Document):
+	pass
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js
index 7658bc9..d7b1c6c 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.js
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.js
@@ -8,6 +8,24 @@
 
 frappe.ui.form.on("Job Applicant", {
 	refresh: function(frm) {
+		frm.set_query("job_title", function() {
+			return {
+				filters: {
+					'status': 'Open'
+				}
+			};
+		});
+		frm.events.create_custom_buttons(frm);
+		frm.events.make_dashboard(frm);
+	},
+
+	create_custom_buttons: function(frm) {
+		if (!frm.doc.__islocal && frm.doc.status !== "Rejected" && frm.doc.status !== "Accepted") {
+			frm.add_custom_button(__("Create Interview"), function() {
+				frm.events.create_dialog(frm);
+			});
+		}
+
 		if (!frm.doc.__islocal) {
 			if (frm.doc.__onload && frm.doc.__onload.job_offer) {
 				$('[data-doctype="Employee Onboarding"]').find("button").show();
@@ -28,14 +46,57 @@
 				});
 			}
 		}
+	},
 
-		frm.set_query("job_title", function() {
-			return {
-				filters: {
-					'status': 'Open'
-				}
-			};
+	make_dashboard: function(frm) {
+		frappe.call({
+			method: "erpnext.hr.doctype.job_applicant.job_applicant.get_interview_details",
+			args: {
+				job_applicant: frm.doc.name
+			},
+			callback: function(r) {
+				$("div").remove(".form-dashboard-section.custom");
+				frm.dashboard.add_section(
+					frappe.render_template('job_applicant_dashboard', {
+						data: r.message
+					}),
+					__("Interview Summary")
+				);
+			}
 		});
+	},
 
+	create_dialog: function(frm) {
+		let d = new frappe.ui.Dialog({
+			title: 'Enter Interview Round',
+			fields: [
+				{
+					label: 'Interview Round',
+					fieldname: 'interview_round',
+					fieldtype: 'Link',
+					options: 'Interview Round'
+				},
+			],
+			primary_action_label: 'Create Interview',
+			primary_action(values) {
+				frm.events.create_interview(frm, values);
+				d.hide();
+			}
+		});
+		d.show();
+	},
+
+	create_interview: function (frm, values) {
+		frappe.call({
+			method: "erpnext.hr.doctype.job_applicant.job_applicant.create_interview",
+			args: {
+				doc: frm.doc,
+				interview_round: values.interview_round
+			},
+			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/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json
index bcea5f5..200f675 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.json
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.json
@@ -9,16 +9,20 @@
  "email_append_to": 1,
  "engine": "InnoDB",
  "field_order": [
+  "details_section",
   "applicant_name",
   "email_id",
   "phone_number",
   "country",
-  "status",
   "column_break_3",
   "job_title",
+  "designation",
+  "status",
+  "source_and_rating_section",
   "source",
   "source_name",
   "employee_referral",
+  "column_break_13",
   "applicant_rating",
   "section_break_6",
   "notes",
@@ -84,7 +88,8 @@
   },
   {
    "fieldname": "section_break_6",
-   "fieldtype": "Section Break"
+   "fieldtype": "Section Break",
+   "label": "Resume"
   },
   {
    "fieldname": "cover_letter",
@@ -160,13 +165,34 @@
    "label": "Employee Referral",
    "options": "Employee Referral",
    "read_only": 1
+  },
+  {
+   "fieldname": "details_section",
+   "fieldtype": "Section Break",
+   "label": "Details"
+  },
+  {
+   "fieldname": "source_and_rating_section",
+   "fieldtype": "Section Break",
+   "label": "Source and Rating"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "job_opening.designation",
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation"
   }
  ],
  "icon": "fa fa-user",
  "idx": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2021-03-24 15:51:11.117517",
+ "modified": "2021-09-29 23:06:10.904260",
  "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 6971e5b..151f492 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant.py
@@ -8,7 +8,9 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
-from frappe.utils import comma_and, validate_email_address
+from frappe.utils import validate_email_address
+
+from erpnext.hr.doctype.interview.interview import get_interviewers
 
 
 class DuplicationError(frappe.ValidationError): pass
@@ -26,7 +28,6 @@
 		self.name = " - ".join(keys)
 
 	def validate(self):
-		self.check_email_id_is_unique()
 		if self.email_id:
 			validate_email_address(self.email_id, True)
 
@@ -44,11 +45,44 @@
 		elif self.status in ["Accepted", "Rejected"]:
 			emp_ref.db_set("status", self.status)
 
+@frappe.whitelist()
+def create_interview(doc, interview_round):
+	import json
 
-	def check_email_id_is_unique(self):
-		if self.email_id:
-			names = frappe.db.sql_list("""select name from `tabJob Applicant`
-				where email_id=%s and name!=%s and job_title=%s""", (self.email_id, self.name, self.job_title))
+	from six import string_types
 
-			if names:
-				frappe.throw(_("Email Address must be unique, already exists for {0}").format(comma_and(names)), frappe.DuplicateEntryError)
+	if isinstance(doc, string_types):
+		doc = json.loads(doc)
+		doc = frappe.get_doc(doc)
+
+	round_designation = frappe.db.get_value("Interview Round", interview_round, "designation")
+
+	if round_designation and doc.designation and round_designation != doc.designation:
+		frappe.throw(_("Interview Round {0} is only applicable for the Designation {1}").format(interview_round, round_designation))
+
+	interview = frappe.new_doc("Interview")
+	interview.interview_round = interview_round
+	interview.job_applicant = doc.name
+	interview.designation = doc.designation
+	interview.resume_link = doc.resume_link
+	interview.job_opening = doc.job_title
+	interviewer_detail = get_interviewers(interview_round)
+
+	for d in interviewer_detail:
+		interview.append("interview_details", {
+			"interviewer": d.interviewer
+		})
+	return interview
+
+@frappe.whitelist()
+def get_interview_details(job_applicant):
+	interview_details = frappe.db.get_all("Interview",
+		filters={"job_applicant":job_applicant, "docstatus": ["!=", 2]},
+		fields=["name", "interview_round", "expected_average_rating", "average_rating", "status"]
+	)
+	interview_detail_map = {}
+
+	for detail in interview_details:
+		interview_detail_map[detail.name] = detail
+
+	return interview_detail_map
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
new file mode 100644
index 0000000..c286787
--- /dev/null
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.html
@@ -0,0 +1,44 @@
+
+{% if not jQuery.isEmptyObject(data) %}
+
+<table class="table table-bordered small">
+	<thead>
+		<tr>
+			<th style="width: 16%" class="text-left">{{ __("Interview") }}</th>
+			<th style="width: 16%" class="text-left">{{ __("Interview Round") }}</th>
+			<th style="width: 12%" class="text-left">{{ __("Status") }}</th>
+			<th style="width: 14%" class="text-left">{{ __("Expected Rating") }}</th>
+			<th style="width: 10%" class="text-left">{{ __("Rating") }}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for(const [key, value] of Object.entries(data)) { %}
+			<tr>
+				<td class="text-left"> {%= key %} </td>
+				<td class="text-left"> {%= value["interview_round"] %} </td>
+				<td class="text-left"> {%= value["status"] %} </td>
+				<td class="text-left">
+					{% for (i = 0; i < value["expected_average_rating"]; i++) { %}
+						<span class="fa fa-star " style="color: #F6C35E;"></span>
+					{% } %}
+					{% for (i = 0; i < (5-value["expected_average_rating"]); i++) { %}
+					<span class="fa fa-star " style="color: #E7E9EB;"></span>
+					{% } %}
+				</td>
+				<td class="text-left">
+					{% if(value["average_rating"]){ %}
+						{% for (i = 0; i < value["average_rating"]; i++) { %}
+							<span class="fa fa-star " style="color: #F6C35E;"></span>
+						{% } %}
+						{% for (i = 0; i < (5-value["average_rating"]); i++) { %}
+						<span class="fa fa-star " style="color: #E7E9EB;"></span>
+						{% } %}
+					{% } %}
+				</td>
+			</tr>
+		{% } %}
+	</tbody>
+</table>
+{% else %}
+<p style="margin-top: 30px;"> No Interview has been scheduled.</p>
+{% endif %}
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
index c005943..2f7795f 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py
@@ -2,14 +2,17 @@
 
 
 def get_data():
-     return {
-        'fieldname': 'job_applicant',
-        'transactions': [
-            {
-                'items': ['Employee', 'Employee Onboarding']
-            },
-            {
-                'items': ['Job Offer']
-            },
-        ],
-    }
+	return {
+		'fieldname': 'job_applicant',
+		'transactions': [
+			{
+				'items': ['Employee', 'Employee Onboarding']
+			},
+			{
+				'items': ['Job Offer', 'Appointment Letter']
+			},
+			{
+				'items': ['Interview']
+			}
+		],
+	}
diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
index e583e25..8fc1290 100644
--- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py
+++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py
@@ -7,7 +7,8 @@
 
 import frappe
 
-# test_records = frappe.get_test_records('Job Applicant')
+from erpnext.hr.doctype.designation.test_designation import create_designation
+
 
 class TestJobApplicant(unittest.TestCase):
 	pass
@@ -25,7 +26,8 @@
 
 	job_applicant = frappe.get_doc({
 		"doctype": "Job Applicant",
-		"status": args.status or "Open"
+		"status": args.status or "Open",
+		"designation":  create_designation().name
 	})
 
 	job_applicant.update(filters)
diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py
index 3f3eca1..162b245 100644
--- a/erpnext/hr/doctype/job_offer/test_job_offer.py
+++ b/erpnext/hr/doctype/job_offer/test_job_offer.py
@@ -32,6 +32,7 @@
 		self.assertTrue(frappe.db.exists("Job Offer", job_offer.name))
 
 	def test_job_applicant_update(self):
+		frappe.db.set_value("HR Settings", None, "check_vacancies", 0)
 		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)
@@ -43,7 +44,11 @@
 		job_offer.status = "Rejected"
 		job_offer.submit()
 		job_applicant.reload()
-		self.assertEqual(job_applicant.status, "Rejected")
+		self.assertEquals(job_applicant.status, "Rejected")
+		frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
+
+	def tearDown(self):
+		frappe.db.sql("DELETE FROM `tabJob Offer` WHERE 1")
 
 def create_job_offer(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 9ccb915..2eaaeec 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -1,8 +1,8 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-cur_frm.add_fetch('employee','employee_name','employee_name');
-cur_frm.add_fetch('employee','company','company');
+cur_frm.add_fetch('employee', 'employee_name', 'employee_name');
+cur_frm.add_fetch('employee', 'company', 'company');
 
 frappe.ui.form.on("Leave Application", {
 	setup: function(frm) {
@@ -19,7 +19,6 @@
 		frm.set_query("employee", erpnext.queries.employee);
 	},
 	onload: function(frm) {
-
 		// Ignore cancellation of doctype on cancel all.
 		frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"];
 
@@ -42,9 +41,9 @@
 	},
 
 	validate: function(frm) {
-		if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){
+		if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1) {
 			frm.doc.half_day_date = frm.doc.from_date;
-		}else if (frm.doc.half_day == 0){
+		} else if (frm.doc.half_day == 0) {
 			frm.doc.half_day_date = "";
 		}
 		frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
@@ -79,14 +78,14 @@
 				__("Allocated Leaves")
 			);
 			frm.dashboard.show();
-			let allowed_leave_types =  Object.keys(leave_details);
+			let allowed_leave_types = Object.keys(leave_details);
 
 			// lwps should be allowed, lwps don't have any allocation
 			allowed_leave_types = allowed_leave_types.concat(lwps);
 
-			frm.set_query('leave_type', function(){
+			frm.set_query('leave_type', function() {
 				return {
-					filters : [
+					filters: [
 						['leave_type_name', 'in', allowed_leave_types]
 					]
 				};
@@ -99,7 +98,7 @@
 			frm.trigger("calculate_total_days");
 		}
 		cur_frm.set_intro("");
-		if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) {
+		if (frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) {
 			frm.set_intro(__("Fill the form and save it"));
 		}
 
@@ -118,7 +117,7 @@
 	},
 
 	leave_approver: function(frm) {
-		if(frm.doc.leave_approver){
+		if (frm.doc.leave_approver) {
 			frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
 		}
 	},
@@ -131,12 +130,10 @@
 		if (frm.doc.half_day) {
 			if (frm.doc.from_date == frm.doc.to_date) {
 				frm.set_value("half_day_date", frm.doc.from_date);
-			}
-			else {
+			} else {
 				frm.trigger("half_day_datepicker");
 			}
-		}
-		else {
+		} else {
 			frm.set_value("half_day_date", "");
 		}
 		frm.trigger("calculate_total_days");
@@ -167,7 +164,7 @@
 	},
 
 	get_leave_balance: function(frm) {
-		if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
+		if (frm.doc.docstatus === 0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
 			return frappe.call({
 				method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on",
 				args: {
@@ -180,8 +177,7 @@
 				callback: function(r) {
 					if (!r.exc && r.message) {
 						frm.set_value('leave_balance', r.message);
-					}
-					else {
+					} else {
 						frm.set_value('leave_balance', "0");
 					}
 				}
@@ -190,12 +186,12 @@
 	},
 
 	calculate_total_days: function(frm) {
-		if(frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) {
+		if (frm.doc.from_date && frm.doc.to_date && frm.doc.employee && frm.doc.leave_type) {
 
 			var from_date = Date.parse(frm.doc.from_date);
 			var to_date = Date.parse(frm.doc.to_date);
 
-			if(to_date < from_date){
+			if (to_date < from_date) {
 				frappe.msgprint(__("To Date cannot be less than From Date"));
 				frm.set_value('to_date', '');
 				return;
@@ -222,7 +218,7 @@
 	},
 
 	set_leave_approver: function(frm) {
-		if(frm.doc.employee) {
+		if (frm.doc.employee) {
 			// server call is done to include holidays in leave days calculations
 			return frappe.call({
 				method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
diff --git a/erpnext/hr/doctype/skill_assessment/__init__.py b/erpnext/hr/doctype/skill_assessment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/__init__.py
diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.json b/erpnext/hr/doctype/skill_assessment/skill_assessment.json
new file mode 100644
index 0000000..8b935c4
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/skill_assessment.json
@@ -0,0 +1,41 @@
+{
+ "actions": [],
+ "creation": "2021-04-12 17:07:39.656289",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "skill",
+  "rating"
+ ],
+ "fields": [
+  {
+   "fieldname": "skill",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Skill",
+   "options": "Skill",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "rating",
+   "fieldtype": "Rating",
+   "in_list_view": 1,
+   "label": "Rating",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-04-12 17:18:14.032298",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Skill Assessment",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/skill_assessment/skill_assessment.py b/erpnext/hr/doctype/skill_assessment/skill_assessment.py
new file mode 100644
index 0000000..3b74c4e
--- /dev/null
+++ b/erpnext/hr/doctype/skill_assessment/skill_assessment.py
@@ -0,0 +1,12 @@
+# -*- 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.model.document import Document
+
+
+class SkillAssessment(Document):
+	pass
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3adc3e9..9e38a9c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -313,3 +313,4 @@
 erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
 erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
 erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
+erpnext.patches.v13_0.add_default_interview_notification_templates
diff --git a/erpnext/patches/v13_0/add_default_interview_notification_templates.py b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
new file mode 100644
index 0000000..5e8a27f
--- /dev/null
+++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py
@@ -0,0 +1,37 @@
+from __future__ import unicode_literals
+
+import os
+
+import frappe
+from frappe import _
+
+
+def execute():
+	if not frappe.db.exists('Email Template', _('Interview Reminder')):
+		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
+		response = frappe.read_file(os.path.join(base_path, 'interview/interview_reminder_notification_template.html'))
+
+		frappe.get_doc({
+			'doctype': 'Email Template',
+			'name': _('Interview Reminder'),
+			'response': response,
+			'subject': _('Interview Reminder'),
+			'owner': frappe.session.user,
+		}).insert(ignore_permissions=True)
+
+	if not frappe.db.exists('Email Template', _('Interview Feedback Reminder')):
+		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
+		response = frappe.read_file(os.path.join(base_path, 'interview/interview_feedback_reminder_template.html'))
+
+		frappe.get_doc({
+			'doctype': 'Email Template',
+			'name': _('Interview Feedback Reminder'),
+			'response': response,
+			'subject': _('Interview Feedback Reminder'),
+			'owner': frappe.session.user,
+		}).insert(ignore_permissions=True)
+
+	hr_settings = frappe.get_doc('HR Settings')
+	hr_settings.interview_reminder_template = _('Interview Reminder')
+	hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder')
+	hr_settings.save()
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index bff36a4..9ed6686 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -171,8 +171,6 @@
 		days_in_month = no_of_days[0]
 		no_of_holidays = no_of_days[1]
 
-		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 1)
-
 		ss.reload()
 		payment_days_based_comp_amount = 0
 		for component in ss.earnings:
diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py
index 6dd0fb1..55d5ec8 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -62,6 +62,13 @@
 	hr_settings.emp_created_by = "Naming Series"
 	hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
 	hr_settings.leave_status_notification_template = _("Leave Status Notification")
+
+	hr_settings.send_interview_reminder = 1
+	hr_settings.interview_reminder_template = _("Interview Reminder")
+	hr_settings.remind_before = "00:15:00"
+
+	hr_settings.send_interview_feedback_reminder = 1
+	hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
 	hr_settings.save()
 
 def set_no_copy_fields_in_variant_settings():
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 907967c..c473395 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -264,16 +264,26 @@
 	base_path = frappe.get_app_path("erpnext", "hr", "doctype")
 	response = frappe.read_file(os.path.join(base_path, "leave_application/leave_application_email_template.html"))
 
-	records += [{'doctype': 'Email Template', 'name': _("Leave Approval Notification"), 'response': response,\
+	records += [{'doctype': 'Email Template', 'name': _("Leave Approval Notification"), 'response': response,
 		'subject': _("Leave Approval Notification"), 'owner': frappe.session.user}]
 
-	records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,\
+	records += [{'doctype': 'Email Template', 'name': _("Leave Status Notification"), 'response': response,
 		'subject': _("Leave Status Notification"), 'owner': frappe.session.user}]
 
+	response = frappe.read_file(os.path.join(base_path, "interview/interview_reminder_notification_template.html"))
+
+	records += [{'doctype': 'Email Template', 'name': _('Interview Reminder'), 'response': response,
+		'subject': _('Interview Reminder'), 'owner': frappe.session.user}]
+
+	response = frappe.read_file(os.path.join(base_path, "interview/interview_feedback_reminder_template.html"))
+
+	records += [{'doctype': 'Email Template', 'name': _('Interview Feedback Reminder'), 'response': response,
+		'subject': _('Interview Feedback Reminder'), 'owner': frappe.session.user}]
+
 	base_path = frappe.get_app_path("erpnext", "stock", "doctype")
 	response = frappe.read_file(os.path.join(base_path, "delivery_trip/dispatch_notification_template.html"))
 
-	records += [{'doctype': 'Email Template', 'name': _("Dispatch Notification"), 'response': response,\
+	records += [{'doctype': 'Email Template', 'name': _("Dispatch Notification"), 'response': response,
 		'subject': _("Your order is out for delivery!"), 'owner': frappe.session.user}]
 
 	# Records for the Supplier Scorecard
@@ -317,6 +327,14 @@
 	hr_settings.emp_created_by = "Naming Series"
 	hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
 	hr_settings.leave_status_notification_template = _("Leave Status Notification")
+
+	hr_settings.send_interview_reminder = 1
+	hr_settings.interview_reminder_template = _("Interview Reminder")
+	hr_settings.remind_before = "00:15:00"
+
+	hr_settings.send_interview_feedback_reminder = 1
+	hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
+
 	hr_settings.save()
 
 def update_item_variant_settings():