fix: Clear Depreciation Schedule before modification (#28507)

diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index c145291..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,47 +0,0 @@
----
-name: Bug report
-about: Report a bug encountered while using ERPNext
-labels: bug
----
-
-<!--
-Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
-
-1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
-    - For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
-    - For documentation issues, refer to https://github.com/frappe/erpnext_com
-2. Use the search function before creating a new issue. Duplicates will be closed and directed to
-   the original discussion.
-3. When making a bug report, make sure you provide all required information. The easier it is for
-   maintainers to reproduce, the faster it'll be fixed.
-4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
--->
-
-## Description of the issue
-
-## Context information (for bug reports)
-
-**Output of `bench version`**
-```
-(paste here)
-```
-
-## Steps to reproduce the issue
-
-1.
-2.
-3.
-
-### Observed result
-
-### Expected result
-
-### Stacktrace / full error message
-
-```
-(paste here)
-```
-
-## Additional information
-
-OS version / distribution, `ERPNext` install method, etc.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
new file mode 100644
index 0000000..a6e16a0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -0,0 +1,106 @@
+name: Bug Report
+description: Report a bug encountered while using ERPNext
+labels: ["bug"]
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
+
+        1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
+            - For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.erpnext.com)
+            - For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly.
+        2. When making a bug report, make sure you provide all required information. The easier it is for
+           maintainers to reproduce, the faster it'll be fixed.
+        3. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
+
+  - type: textarea
+    id: bug-info
+    attributes:
+      label: Information about bug
+      description: Also tell us, what did you expect to happen?
+      placeholder: Please provide as much information as possible.
+    validations:
+      required: true
+
+  - type: dropdown
+    id: version
+    attributes:
+      label: Version
+      description: Affected versions.
+      multiple: true
+      options:
+        - v12
+        - v13
+        - v14
+        - develop
+    validations:
+      required: true
+
+  - type: dropdown
+    id: module
+    attributes:
+      label: Module
+      description: Select affected module of ERPNext.
+      multiple: true
+      options:
+        - accounts
+        - stock
+        - buying
+        - selling
+        - ecommerce
+        - manufacturing
+        - HR
+        - projects
+        - support
+        - assets
+        - integrations
+        - quality
+        - regional
+        - portal
+        - agriculture
+        - education
+        - non-profit
+    validations:
+      required: true
+
+  - type: textarea
+    id: exact-version
+    attributes:
+      label: Version
+      description: Share exact version number of Frappe and ERPNext you are using.
+      placeholder: |
+        Frappe version -
+        ERPNext Verion -
+    validations:
+      required: true
+
+  - type: dropdown
+    id: install-method
+    attributes:
+      label: Installation method
+      options:
+        - docker
+        - easy-install
+        - manual install
+        - FrappeCloud
+    validations:
+      required: true
+
+  - type: textarea
+    id: logs
+    attributes:
+      label: Relevant log output / Stack trace / Full Error Message.
+      description: Please copy and paste any relevant log output. This will be automatically formatted.
+      render: shell
+
+
+  - type: checkboxes
+    id: terms
+    attributes:
+      label: Code of Conduct
+      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)
+      options:
+        - label: I agree to follow this project's Code of Conduct
+          required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 6cdad35..418bf3c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,7 +1,10 @@
 ---
 name: Feature request
 about: Suggest an idea to improve ERPNext
+title: ''
 labels: feature-request
+assignees: ''
+
 ---
 
 <!--
diff --git a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md b/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md
deleted file mode 100644
index 2016bcc..0000000
--- a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-name: Question about using ERPNext
-about: This is not the appropriate channel
-labels: invalid
----
-
-Please post on our forums:
-
-for questions about using `ERPNext`: https://discuss.erpnext.com
-
-for questions about using the `Frappe Framework`: ~~https://discuss.frappe.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/frappe) tagged under `frappe`
-
-for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench)
-
-For documentation issues, use the [ERPNext Documentation](https://erpnext.com/docs/) or [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet)
-
-> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**
\ No newline at end of file
diff --git a/.github/stale.yml b/.github/stale.yml
index 9322ae8..8b7cb9b 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,34 +1,36 @@
 # Configuration for probot-stale - https://github.com/probot/stale
 
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 15
+# Label to use when marking as stale
+staleLabel: inactive
 
-# Number of days of inactivity before a stale Issue or Pull Request is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 3
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
-  - hotfix
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 10
 
 # Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
+exemptProjects: true
 
 # Set to true to ignore issues in a milestone (defaults to false)
 exemptMilestones: true
 
-# Label to use when marking as stale
-staleLabel: inactive
+pulls:
+  daysUntilStale: 15
+  daysUntilClose: 3
+  exemptLabels:
+    - hotfix
+  markComment: >
+    This pull request has been automatically marked as inactive because it has
+    not had recent activity. It will be closed within 3 days if no further
+    activity occurs, but it only takes a comment to keep a contribution alive
+    :) Also, even if it is closed, you can always reopen the PR when you're
+    ready. Thank you for contributing.
 
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: >
-  This pull request has been automatically marked as stale because it has not had
-  recent activity. It will be closed within a week if no further activity occurs, but it
-  only takes a comment to keep a contribution alive :) Also, even if it is closed,
-  you can always reopen the PR when you're ready. Thank you for contributing.
-  
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 30
-
-# Limit to only `issues` or `pulls`
-only: pulls
+issues:
+  daysUntilStale: 60
+  daysUntilClose: 7
+  exemptLabels:
+    - valid
+    - to-validate
+  markComment: >
+    This issue has been automatically marked as inactive because it has not had
+    recent activity and it wasn't validated by maintainer team. It will be
+    closed within a week if no further activity occurs.
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index 7451917..4839207 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -10,6 +10,8 @@
 from frappe.model.document import Document
 from frappe.utils import cint
 
+from erpnext.stock.utils import check_pending_reposting
+
 
 class AccountsSettings(Document):
 	def on_update(self):
@@ -25,6 +27,7 @@
 		self.validate_stale_days()
 		self.enable_payment_schedule_in_print()
 		self.toggle_discount_accounting_fields()
+		self.validate_pending_reposts()
 
 	def validate_stale_days(self):
 		if not self.allow_stale and cint(self.stale_days) <= 0:
@@ -56,3 +59,8 @@
 				make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
 
 		make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
+
+
+	def validate_pending_reposts(self):
+		if self.acc_frozen_upto:
+			check_pending_reposting(self.acc_frozen_upto)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 48b5cb9..df957d2 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -114,6 +114,9 @@
 		self.set_status()
 		self.validate_purchase_receipt_if_update_stock()
 		validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
+		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
 
 	def validate_release_date(self):
 		if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
@@ -294,8 +297,15 @@
 						item.expense_account = stock_not_billed_account
 
 			elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
-				item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
+				asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
 					company = self.company)
+				if not asset_category_account:
+					form_link = get_link_to_form('Asset Category', asset_category)
+					throw(
+						_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
+						title=_("Missing Account")
+					)
+				item.expense_account = asset_category_account
 			elif item.is_fixed_asset and item.pr_detail:
 				item.expense_account = asset_received_but_not_billed
 			elif not item.expense_account and for_validate:
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index c4d59f1..64712b5 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -155,6 +155,8 @@
 		if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points and not self.is_consolidated:
 			validate_loyalty_points(self, self.loyalty_points)
 
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+
 	def validate_fixed_asset(self):
 		for d in self.get("items"):
 			if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 353f908..a990f23 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -545,7 +545,9 @@
 
 	def set_ageing(self, row):
 		if self.filters.ageing_based_on == "Due Date":
-			entry_date = row.due_date
+			# use posting date as a fallback for advances posted via journal and payment entry
+			# when ageing viewed by due date
+			entry_date = row.due_date or row.posting_date
 		elif self.filters.ageing_based_on == "Supplier Invoice Date":
 			entry_date = row.bill_date
 		else:
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index a3a45d1..caee1a1 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -36,12 +36,16 @@
 			posting_date = entry.posting_date
 			voucher_type = entry.voucher_type
 
+			if not tax_withholding_category:
+				tax_withholding_category = supplier_map.get(supplier, {}).get('tax_withholding_category')
+				rate = tax_rate_map.get(tax_withholding_category)
+
 			if entry.account in tds_accounts:
 				tds_deducted += (entry.credit - entry.debit)
 
 			total_amount_credited += (entry.credit - entry.debit)
 
-		if rate and tds_deducted:
+		if tds_deducted:
 			row = {
 				'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
 				'supplier': supplier_map.get(supplier, {}).get('name')
@@ -67,7 +71,7 @@
 
 def get_supplier_pan_map():
 	supplier_map = frappe._dict()
-	suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
+	suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name', 'tax_withholding_category'])
 
 	for d in suppliers:
 		supplier_map[d.name] = d
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 5eab21b..1b5f35e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -72,6 +72,7 @@
 		self.create_raw_materials_supplied("supplied_items")
 		self.set_received_qty_for_drop_ship_items()
 		validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference)
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def validate_with_previous_doc(self):
 		super(PurchaseOrder, self).validate_with_previous_doc({
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 0a51a8e..023c95d 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -17,6 +17,7 @@
   "company",
   "transaction_date",
   "valid_till",
+  "quotation_number",
   "amended_from",
   "address_section",
   "supplier_address",
@@ -797,6 +798,11 @@
    "fieldtype": "Date",
    "in_list_view": 1,
    "label": "Valid Till"
+  },
+  {
+   "fieldname": "quotation_number",
+   "fieldtype": "Data",
+   "label": "Quotation Number"
   }
  ],
  "icon": "fa fa-shopping-cart",
@@ -804,10 +810,11 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-19 00:58:20.995491",
+ "modified": "2021-12-11 06:43:20.924080",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/controllers/tests/test_transaction_base.py b/erpnext/controllers/tests/test_transaction_base.py
new file mode 100644
index 0000000..13aa697
--- /dev/null
+++ b/erpnext/controllers/tests/test_transaction_base.py
@@ -0,0 +1,22 @@
+import unittest
+
+import frappe
+
+
+class TestUtils(unittest.TestCase):
+    def test_reset_default_field_value(self):
+        doc = frappe.get_doc({
+            "doctype": "Purchase Receipt",
+            "set_warehouse": "Warehouse 1",
+        })
+
+        # Same values
+        doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}]
+        doc.reset_default_field_value("set_warehouse", "items", "warehouse")
+        self.assertEqual(doc.set_warehouse, "Warehouse 1")
+
+        # Mixed values
+        doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}]
+        doc.reset_default_field_value("set_warehouse", "items", "warehouse")
+        self.assertEqual(doc.set_warehouse, None)
+
diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json
index 95b19fa..8f0fa31 100644
--- a/erpnext/crm/doctype/crm_settings/crm_settings.json
+++ b/erpnext/crm/doctype/crm_settings/crm_settings.json
@@ -91,8 +91,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "migration_hash": "3ae78b12dd1c64d551736c6e82092f90",
- "modified": "2021-11-03 09:00:36.883496",
+ "modified": "2021-11-03 10:00:36.883496",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "CRM Settings",
diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json
index feb6044..dc32d9a 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.json
+++ b/erpnext/crm/doctype/opportunity/opportunity.json
@@ -510,8 +510,7 @@
  "icon": "fa fa-info-sign",
  "idx": 195,
  "links": [],
- "migration_hash": "d87c646ea2579b6900197fd41e6c5c5a",
- "modified": "2021-10-21 11:04:30.151379",
+ "modified": "2021-10-21 12:04:30.151379",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Opportunity",
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
index ae1f36e..6afd3f7 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
@@ -115,8 +115,7 @@
  ],
  "issingle": 1,
  "links": [],
- "migration_hash": "8ca1ea3309ed28547b19da8e6e27e96f",
- "modified": "2021-11-30 11:17:24.647979",
+ "modified": "2021-11-30 12:17:24.647979",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "TaxJar Settings",
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 05c46c5..9ceb626 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -234,7 +234,7 @@
 	},
 	"Communication": {
 		"on_update": [
-			"erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time",
+			"erpnext.support.doctype.service_level_agreement.service_level_agreement.on_communication_update",
 			"erpnext.support.doctype.issue.issue.set_first_response_time"
 		]
 	},
@@ -265,6 +265,9 @@
 			"erpnext.regional.india.utils.update_taxable_values"
 		]
 	},
+	"POS Invoice": {
+		"on_submit": ["erpnext.regional.saudi_arabia.utils.create_qr_code"]
+	},
 	"Purchase Invoice": {
 		"validate": [
 			"erpnext.regional.india.utils.validate_reverse_charge_transaction",
@@ -340,8 +343,7 @@
 		"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
 		"erpnext.projects.doctype.project.project.hourly_reminder",
 		"erpnext.projects.doctype.project.project.collect_project_status",
-		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
-		"erpnext.support.doctype.service_level_agreement.service_level_agreement.set_service_level_agreement_variance"
+		"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts"
 	],
 	"hourly_long": [
 		"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index a4c0af0..a1247d9 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -21,7 +21,11 @@
 			},
 			{
 				'label': _('Lifecycle'),
-				'items': ['Employee Transfer', 'Employee Promotion', 'Employee Separation', 'Employee Grievance']
+				'items': ['Employee Transfer', 'Employee Promotion', 'Employee Grievance']
+			},
+			{
+				'label': _('Exit'),
+				'items': ['Employee Separation', 'Exit Interview', 'Full and Final Statement']
 			},
 			{
 				'label': _('Shift'),
diff --git a/erpnext/hr/doctype/exit_interview/__init__.py b/erpnext/hr/doctype/exit_interview/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/__init__.py
diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.js b/erpnext/hr/doctype/exit_interview/exit_interview.js
new file mode 100644
index 0000000..502af42
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/exit_interview.js
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Exit Interview', {
+	refresh: function(frm) {
+		if (!frm.doc.__islocal && !frm.doc.questionnaire_email_sent && frappe.boot.user.can_write.includes('Exit Interview')) {
+			frm.add_custom_button(__('Send Exit Questionnaire'), function () {
+				frm.trigger('send_exit_questionnaire');
+			});
+		}
+	},
+
+	employee: function(frm) {
+		frappe.db.get_value('Employee', frm.doc.employee, 'relieving_date', (message) => {
+			if (!message.relieving_date) {
+				frappe.throw({
+					message: __('Please set the relieving date for employee {0}',
+						['<a href="/app/employee/' + frm.doc.employee +'">' + frm.doc.employee + '</a>']),
+					title: __('Relieving Date Missing')
+				});
+			}
+		});
+	},
+
+	send_exit_questionnaire: function(frm) {
+		frappe.call({
+			method: 'erpnext.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire',
+			args: {
+				'interviews': [frm.doc]
+			},
+			callback: function(r) {
+				if (!r.exc) {
+					frm.refresh_field('questionnaire_email_sent');
+				}
+			}
+		});
+	}
+});
diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.json b/erpnext/hr/doctype/exit_interview/exit_interview.json
new file mode 100644
index 0000000..989a1b8
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/exit_interview.json
@@ -0,0 +1,246 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "naming_series:",
+ "creation": "2021-12-05 13:56:36.241690",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "email_append_to": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "employee",
+  "employee_name",
+  "email",
+  "column_break_5",
+  "company",
+  "status",
+  "date",
+  "employee_details_section",
+  "department",
+  "designation",
+  "reports_to",
+  "column_break_9",
+  "date_of_joining",
+  "relieving_date",
+  "exit_questionnaire_section",
+  "ref_doctype",
+  "questionnaire_email_sent",
+  "column_break_10",
+  "reference_document_name",
+  "interview_summary_section",
+  "interviewers",
+  "interview_summary",
+  "employee_status_section",
+  "employee_status",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.relieving_date",
+   "fieldname": "relieving_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Relieving Date",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Date",
+   "mandatory_depends_on": "eval:doc.status==='Scheduled';"
+  },
+  {
+   "fieldname": "exit_questionnaire_section",
+   "fieldtype": "Section Break",
+   "label": "Exit Questionnaire"
+  },
+  {
+   "fieldname": "ref_doctype",
+   "fieldtype": "Link",
+   "label": "Reference Document Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "reference_document_name",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Reference Document Name",
+   "options": "ref_doctype"
+  },
+  {
+   "fieldname": "interview_summary_section",
+   "fieldtype": "Section Break",
+   "label": "Interview Details"
+  },
+  {
+   "fieldname": "column_break_10",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "interviewers",
+   "fieldtype": "Table MultiSelect",
+   "label": "Interviewers",
+   "mandatory_depends_on": "eval:doc.status==='Scheduled';",
+   "options": "Interviewer"
+  },
+  {
+   "fetch_from": "employee.date_of_joining",
+   "fieldname": "date_of_joining",
+   "fieldtype": "Date",
+   "label": "Date of Joining",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.reports_to",
+   "fieldname": "reports_to",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Reports To",
+   "options": "Employee",
+   "read_only": 1
+  },
+  {
+   "fieldname": "employee_details_section",
+   "fieldtype": "Section Break",
+   "label": "Employee Details"
+  },
+  {
+   "fetch_from": "employee.designation",
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "HR-EXIT-INT-"
+  },
+  {
+   "default": "0",
+   "fieldname": "questionnaire_email_sent",
+   "fieldtype": "Check",
+   "in_standard_filter": 1,
+   "label": "Questionnaire Email Sent",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "email",
+   "fieldtype": "Data",
+   "label": "Email ID",
+   "options": "Email",
+   "read_only": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "options": "Pending\nScheduled\nCompleted\nCancelled",
+   "reqd": 1
+  },
+  {
+   "fieldname": "employee_status_section",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "employee_status",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Final Decision",
+   "mandatory_depends_on": "eval:doc.status==='Completed';",
+   "options": "\nEmployee Retained\nExit Confirmed"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Exit Interview",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "interview_summary",
+   "fieldtype": "Text Editor",
+   "label": "Interview Summary"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-12-07 23:39:22.645401",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Exit Interview",
+ "naming_rule": "By \"Naming Series\" field",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sender_field": "email",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/exit_interview/exit_interview.py b/erpnext/hr/doctype/exit_interview/exit_interview.py
new file mode 100644
index 0000000..30e19f1
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/exit_interview.py
@@ -0,0 +1,131 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import get_link_to_form
+
+from erpnext.hr.doctype.employee.employee import get_employee_email
+
+
+class ExitInterview(Document):
+	def validate(self):
+		self.validate_relieving_date()
+		self.validate_duplicate_interview()
+		self.set_employee_email()
+
+	def validate_relieving_date(self):
+		if not frappe.db.get_value('Employee', self.employee, 'relieving_date'):
+			frappe.throw(_('Please set the relieving date for employee {0}').format(
+				get_link_to_form('Employee', self.employee)),
+				title=_('Relieving Date Missing'))
+
+	def validate_duplicate_interview(self):
+		doc = frappe.db.exists('Exit Interview', {
+			'employee': self.employee,
+			'name': ('!=', self.name),
+			'docstatus': ('!=', 2)
+		})
+		if doc:
+			frappe.throw(_('Exit Interview {0} already exists for Employee: {1}').format(
+				get_link_to_form('Exit Interview', doc), frappe.bold(self.employee)),
+				frappe.DuplicateEntryError)
+
+	def set_employee_email(self):
+		employee = frappe.get_doc('Employee', self.employee)
+		self.email = get_employee_email(employee)
+
+	def on_submit(self):
+		if self.status != 'Completed':
+			frappe.throw(_('Only Completed documents can be submitted'))
+
+		self.update_interview_date_in_employee()
+
+	def on_cancel(self):
+		self.update_interview_date_in_employee()
+		self.db_set('status', 'Cancelled')
+
+	def update_interview_date_in_employee(self):
+		if self.docstatus == 1:
+			frappe.db.set_value('Employee', self.employee, 'held_on', self.date)
+		elif self.docstatus == 2:
+			frappe.db.set_value('Employee', self.employee, 'held_on', None)
+
+
+@frappe.whitelist()
+def send_exit_questionnaire(interviews):
+	interviews = get_interviews(interviews)
+	validate_questionnaire_settings()
+
+	email_success = []
+	email_failure = []
+
+	for exit_interview in interviews:
+		interview = frappe.get_doc('Exit Interview', exit_interview.get('name'))
+		if interview.get('questionnaire_email_sent'):
+			continue
+
+		employee = frappe.get_doc('Employee', interview.employee)
+		email = get_employee_email(employee)
+
+		context = interview.as_dict()
+		context.update(employee.as_dict())
+		template_name = frappe.db.get_single_value('HR Settings', 'exit_questionnaire_notification_template')
+		template = frappe.get_doc('Email Template', template_name)
+
+		if email:
+			frappe.sendmail(
+				recipients=email,
+				subject=template.subject,
+				message=frappe.render_template(template.response, context),
+				reference_doctype=interview.doctype,
+				reference_name=interview.name
+			)
+			interview.db_set('questionnaire_email_sent', True)
+			interview.notify_update()
+			email_success.append(email)
+		else:
+			email_failure.append(get_link_to_form('Employee', employee.name))
+
+	show_email_summary(email_success, email_failure)
+
+
+def get_interviews(interviews):
+	import json
+
+	if isinstance(interviews, str):
+		interviews = json.loads(interviews)
+
+	if not len(interviews):
+		frappe.throw(_('Atleast one interview has to be selected.'))
+
+	return interviews
+
+
+def validate_questionnaire_settings():
+	settings = frappe.db.get_value('HR Settings', 'HR Settings',
+		['exit_questionnaire_web_form', 'exit_questionnaire_notification_template'], as_dict=True)
+
+	if not settings.exit_questionnaire_web_form or not settings.exit_questionnaire_notification_template:
+		frappe.throw(
+			_('Please set {0} and {1} in {2}.').format(
+				frappe.bold('Exit Questionnaire Web Form'),
+				frappe.bold('Notification Template'),
+				get_link_to_form('HR Settings', 'HR Settings')),
+			title=_('Settings Missing')
+		)
+
+
+def show_email_summary(email_success, email_failure):
+	message = ''
+	if email_success:
+		message += _('{0}: {1}').format(
+			frappe.bold('Sent Successfully'), ', '.join(email_success))
+	if message and email_failure:
+		message += '<br><br>'
+	if email_failure:
+		message += _('{0} due to missing email information for employee(s): {1}').format(
+			frappe.bold('Sending Failed'), ', '.join(email_failure))
+
+	frappe.msgprint(message, title=_('Exit Questionnaire'), indicator='blue', is_minimizable=True, wide=True)
\ No newline at end of file
diff --git a/erpnext/hr/doctype/exit_interview/exit_interview_list.js b/erpnext/hr/doctype/exit_interview/exit_interview_list.js
new file mode 100644
index 0000000..93d7b21
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/exit_interview_list.js
@@ -0,0 +1,27 @@
+frappe.listview_settings['Exit Interview'] = {
+	has_indicator_for_draft: 1,
+	get_indicator: function(doc) {
+		let status_color = {
+			'Pending': 'orange',
+			'Scheduled': 'yellow',
+			'Completed': 'green',
+			'Cancelled': 'red',
+		};
+		return [__(doc.status), status_color[doc.status], 'status,=,'+doc.status];
+	},
+
+	onload: function(listview) {
+		if (frappe.boot.user.can_write.includes('Exit Interview')) {
+			listview.page.add_action_item(__('Send Exit Questionnaires'), function() {
+				const interviews = listview.get_checked_items();
+				frappe.call({
+					method: 'erpnext.hr.doctype.exit_interview.exit_interview.send_exit_questionnaire',
+					freeze: true,
+					args: {
+						'interviews': interviews
+					}
+				});
+			});
+		}
+	}
+};
diff --git a/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html b/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html
new file mode 100644
index 0000000..0317b1a
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/exit_questionnaire_notification_template.html
@@ -0,0 +1,16 @@
+<h2>Exit Questionnaire</h2>
+<br>
+
+<p>
+	Dear {{ employee_name }},
+	<br><br>
+
+	Thank you for the contribution you have made during your time at {{ company }}. We value your opinion and welcome the feedback on your experience working with us.
+	Request you to take out a few minutes to fill up this Exit Questionnaire.
+
+	{% set web_form = frappe.db.get_value('HR Settings', 'HR Settings', 'exit_questionnaire_web_form') %}
+	{% set web_form_link = frappe.utils.get_url(uri=frappe.db.get_value('Web Form', web_form, 'route')) %}
+
+	<br><br>
+	<a class="btn btn-primary" href="{{ web_form_link }}" target="_blank">{{ _('Submit Now') }}</a>
+</p>
diff --git a/erpnext/hr/doctype/exit_interview/test_exit_interview.py b/erpnext/hr/doctype/exit_interview/test_exit_interview.py
new file mode 100644
index 0000000..a0bf9b3
--- /dev/null
+++ b/erpnext/hr/doctype/exit_interview/test_exit_interview.py
@@ -0,0 +1,118 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import os
+import unittest
+
+import frappe
+from frappe import _
+from frappe.core.doctype.user_permission.test_user_permission import create_user
+from frappe.tests.test_webform import create_custom_doctype, create_webform
+from frappe.utils import getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.exit_interview.exit_interview import send_exit_questionnaire
+
+
+class TestExitInterview(unittest.TestCase):
+	def setUp(self):
+		frappe.db.sql('delete from `tabExit Interview`')
+
+	def test_duplicate_interview(self):
+		employee = make_employee('employeeexit1@example.com')
+		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		interview = create_exit_interview(employee)
+
+		doc = frappe.copy_doc(interview)
+		self.assertRaises(frappe.DuplicateEntryError, doc.save)
+
+	def test_relieving_date_validation(self):
+		employee = make_employee('employeeexit2@example.com')
+		# unset relieving date
+		frappe.db.set_value('Employee', employee, 'relieving_date', None)
+
+		interview = create_exit_interview(employee, save=False)
+		self.assertRaises(frappe.ValidationError, interview.save)
+
+		# set relieving date
+		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+		interview = create_exit_interview(employee)
+		self.assertTrue(interview.name)
+
+	def test_interview_date_updated_in_employee_master(self):
+		employee = make_employee('employeeexit3@example.com')
+		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+
+		interview = create_exit_interview(employee)
+		interview.status = 'Completed'
+		interview.employee_status = 'Exit Confirmed'
+
+		# exit interview date updated on submit
+		interview.submit()
+		self.assertEqual(frappe.db.get_value('Employee', employee, 'held_on'), interview.date)
+
+		# exit interview reset on cancel
+		interview.reload()
+		interview.cancel()
+		self.assertEqual(frappe.db.get_value('Employee', employee, 'held_on'), None)
+
+	def test_send_exit_questionnaire(self):
+		create_custom_doctype()
+		create_webform()
+		template = create_notification_template()
+
+		webform = frappe.db.get_all('Web Form', limit=1)
+		frappe.db.set_value('HR Settings', 'HR Settings', {
+			'exit_questionnaire_web_form': webform[0].name,
+			'exit_questionnaire_notification_template': template
+		})
+
+		employee = make_employee('employeeexit3@example.com')
+		frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
+
+		interview = create_exit_interview(employee)
+		send_exit_questionnaire([interview])
+
+		email_queue = frappe.db.get_all('Email Queue', ['name', 'message'], limit=1)
+		self.assertTrue('Subject: Exit Questionnaire Notification' in email_queue[0].message)
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+
+def create_exit_interview(employee, save=True):
+	interviewer = create_user('test_exit_interviewer@example.com')
+
+	doc = frappe.get_doc({
+		'doctype': 'Exit Interview',
+		'employee': employee,
+		'company': '_Test Company',
+		'status': 'Pending',
+		'date': getdate(),
+		'interviewers': [{
+			'interviewer': interviewer.name
+		}],
+		'interview_summary': 'Test'
+	})
+
+	if save:
+		return doc.insert()
+	return doc
+
+
+def create_notification_template():
+	template = frappe.db.exists('Email Template', _('Exit Questionnaire Notification'))
+	if not template:
+		base_path = frappe.get_app_path('erpnext', 'hr', 'doctype')
+		response = frappe.read_file(os.path.join(base_path, 'exit_interview/exit_questionnaire_notification_template.html'))
+
+		template = frappe.get_doc({
+			'doctype': 'Email Template',
+			'name': _('Exit Questionnaire Notification'),
+			'response': response,
+			'subject': _('Exit Questionnaire Notification'),
+			'owner': frappe.session.user,
+		}).insert(ignore_permissions=True)
+		template = template.name
+
+	return template
\ No newline at end of file
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 5148435..f9a3e05 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -36,7 +36,11 @@
   "remind_before",
   "column_break_4",
   "send_interview_feedback_reminder",
-  "feedback_reminder_notification_template"
+  "feedback_reminder_notification_template",
+  "employee_exit_section",
+  "exit_questionnaire_web_form",
+  "column_break_34",
+  "exit_questionnaire_notification_template"
  ],
  "fields": [
   {
@@ -226,13 +230,34 @@
    "fieldname": "check_vacancies",
    "fieldtype": "Check",
    "label": "Check Vacancies On Job Offer Creation"
+  },
+  {
+   "fieldname": "employee_exit_section",
+   "fieldtype": "Section Break",
+   "label": "Employee Exit Settings"
+  },
+  {
+   "fieldname": "exit_questionnaire_web_form",
+   "fieldtype": "Link",
+   "label": "Exit Questionnaire Web Form",
+   "options": "Web Form"
+  },
+  {
+   "fieldname": "exit_questionnaire_notification_template",
+   "fieldtype": "Link",
+   "label": "Exit Questionnaire Notification Template",
+   "options": "Email Template"
+  },
+  {
+   "fieldname": "column_break_34",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-cog",
  "idx": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-10-01 23:46:11.098236",
+ "modified": "2021-12-05 14:48:10.884253",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR Settings",
diff --git a/erpnext/hr/notification/exit_interview_scheduled/__init__.py b/erpnext/hr/notification/exit_interview_scheduled/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/notification/exit_interview_scheduled/__init__.py
diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json
new file mode 100644
index 0000000..8323ef0
--- /dev/null
+++ b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.json
@@ -0,0 +1,29 @@
+{
+ "attach_print": 0,
+ "channel": "Email",
+ "condition": "doc.date and doc.email and doc.docstatus != 2 and doc.status == 'Scheduled'",
+ "creation": "2021-12-05 22:11:47.263933",
+ "date_changed": "date",
+ "days_in_advance": 1,
+ "docstatus": 0,
+ "doctype": "Notification",
+ "document_type": "Exit Interview",
+ "enabled": 1,
+ "event": "Days Before",
+ "idx": 0,
+ "is_standard": 1,
+ "message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n\t<tr height=\"10\"></tr>\n\t<tr>\n\t\t<td width=\"15\"></td>\n\t\t<td>\n\t\t\t<div class=\"text-medium text-muted\">\n\t\t\t\t<span>{{_(\"Exit Interview Scheduled:\")}} {{ doc.name }}</span>\n\t\t\t</div>\n\t\t</td>\n\t\t<td width=\"15\"></td>\n\t</tr>\n\t<tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n\t<tr height=\"10\"></tr>\n\t<tr>\n\t\t<td width=\"15\"></td>\n\t\t<td>\n\t\t\t<div>\n\t\t\t\t<ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n\t\t\t\t\t<li>{{_(\"Employee\")}}: <b>{{ doc.employee }} - {{ doc.employee_name }}</b></li>\n\t\t\t\t\t<li>{{_(\"Date\")}}: <b>{{ doc.date }}</b></li>\n\t\t\t\t\t<li> {{_(\"Interviewers\")}}: </li>\n\t\t\t\t\t{% for entry in doc.interviewers %}\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li>{{ entry.user }}</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t{% endfor %}\n\t\t\t\t\t<li>{{ _(\"Interview Document\") }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</td>\n\t\t<td width=\"15\"></td>\n\t</tr>\n\t<tr height=\"10\"></tr>\n</table>\n",
+ "modified": "2021-12-05 22:26:57.096159",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Exit Interview Scheduled",
+ "owner": "Administrator",
+ "recipients": [
+  {
+   "receiver_by_document_field": "email"
+  }
+ ],
+ "send_system_notification": 0,
+ "send_to_all_assignees": 1,
+ "subject": "Exit Interview Scheduled: {{ doc.name }}"
+}
\ No newline at end of file
diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md
new file mode 100644
index 0000000..6d6db40
--- /dev/null
+++ b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.md
@@ -0,0 +1,37 @@
+<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
+	<tr height="10"></tr>
+	<tr>
+		<td width="15"></td>
+		<td>
+			<div class="text-medium text-muted">
+				<h2>{{_("Exit Interview Scheduled:")}} {{ doc.name }}</h2>
+			</div>
+		</td>
+		<td width="15"></td>
+	</tr>
+	<tr height="10"></tr>
+</table>
+
+<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
+	<tr height="10"></tr>
+	<tr>
+		<td width="15"></td>
+		<td>
+			<div>
+				<ul class="list-unstyled" style="line-height: 1.7">
+					<li><b>{{_("Employee")}}: </b>{{ doc.employee }} - {{ doc.employee_name }}</li>
+					<li><b>{{_("Date")}}: </b>{{ frappe.utils.formatdate(doc.date) }}</li>
+					<li><b>{{_("Interviewers")}}:</b> </li>
+					{% for entry in doc.interviewers %}
+						<ul>
+							<li>{{ entry.user }}</li>
+						</ul>
+					{% endfor %}
+					<li><b>{{ _("Interview Document") }}:</b> {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
+				</ul>
+			</div>
+		</td>
+		<td width="15"></td>
+	</tr>
+	<tr height="10"></tr>
+</table>
diff --git a/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py
new file mode 100644
index 0000000..5f697c9
--- /dev/null
+++ b/erpnext/hr/notification/exit_interview_scheduled/exit_interview_scheduled.py
@@ -0,0 +1,6 @@
+# import frappe
+
+
+def get_context(context):
+	# do your magic here
+	pass
diff --git a/erpnext/hr/report/employee_exits/__init__.py b/erpnext/hr/report/employee_exits/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/report/employee_exits/__init__.py
diff --git a/erpnext/hr/report/employee_exits/employee_exits.js b/erpnext/hr/report/employee_exits/employee_exits.js
new file mode 100644
index 0000000..ac677d8
--- /dev/null
+++ b/erpnext/hr/report/employee_exits/employee_exits.js
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Employee Exits"] = {
+	filters: [
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12)
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.nowdate()
+		},
+		{
+			"fieldname": "company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company"
+		},
+		{
+			"fieldname": "department",
+			"label": __("Department"),
+			"fieldtype": "Link",
+			"options": "Department"
+		},
+		{
+			"fieldname": "designation",
+			"label": __("Designation"),
+			"fieldtype": "Link",
+			"options": "Designation"
+		},
+		{
+			"fieldname": "employee",
+			"label": __("Employee"),
+			"fieldtype": "Link",
+			"options": "Employee"
+		},
+		{
+			"fieldname": "reports_to",
+			"label": __("Reports To"),
+			"fieldtype": "Link",
+			"options": "Employee"
+		},
+		{
+			"fieldname": "interview_status",
+			"label": __("Interview Status"),
+			"fieldtype": "Select",
+			"options": ["", "Pending", "Scheduled", "Completed"]
+		},
+		{
+			"fieldname": "final_decision",
+			"label": __("Final Decision"),
+			"fieldtype": "Select",
+			"options": ["", "Employee Retained", "Exit Confirmed"]
+		},
+		{
+			"fieldname": "exit_interview_pending",
+			"label": __("Exit Interview Pending"),
+			"fieldtype": "Check"
+		},
+		{
+			"fieldname": "questionnaire_pending",
+			"label": __("Exit Questionnaire Pending"),
+			"fieldtype": "Check"
+		},
+		{
+			"fieldname": "fnf_pending",
+			"label": __("FnF Pending"),
+			"fieldtype": "Check"
+		}
+	]
+};
diff --git a/erpnext/hr/report/employee_exits/employee_exits.json b/erpnext/hr/report/employee_exits/employee_exits.json
new file mode 100644
index 0000000..4fe9a85
--- /dev/null
+++ b/erpnext/hr/report/employee_exits/employee_exits.json
@@ -0,0 +1,33 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-12-05 19:47:18.332319",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Test",
+ "modified": "2021-12-05 19:47:18.332319",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Employee Exits",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Exit Interview",
+ "report_name": "Employee Exits",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "HR User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/hr/report/employee_exits/employee_exits.py b/erpnext/hr/report/employee_exits/employee_exits.py
new file mode 100644
index 0000000..8e0b07d
--- /dev/null
+++ b/erpnext/hr/report/employee_exits/employee_exits.py
@@ -0,0 +1,231 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# License: MIT. See LICENSE
+
+import frappe
+from frappe import _
+from frappe.query_builder import Order
+from frappe.utils import getdate
+
+
+def execute(filters=None):
+	columns = get_columns()
+	data = get_data(filters)
+	chart = get_chart_data(data)
+	report_summary = get_report_summary(data)
+
+	return columns, data, None, chart, report_summary
+
+def get_columns():
+	return [
+		{
+			'label': _('Employee'),
+			'fieldname': 'employee',
+			'fieldtype': 'Link',
+			'options': 'Employee',
+			'width': 150
+		},
+		{
+			'label': _('Employee Name'),
+			'fieldname': 'employee_name',
+			'fieldtype': 'Data',
+			'width': 150
+		},
+		{
+			'label': _('Date of Joining'),
+			'fieldname': 'date_of_joining',
+			'fieldtype': 'Date',
+			'width': 120
+		},
+		{
+			'label': _('Relieving Date'),
+			'fieldname': 'relieving_date',
+			'fieldtype': 'Date',
+			'width': 120
+		},
+		{
+			'label': _('Exit Interview'),
+			'fieldname': 'exit_interview',
+			'fieldtype': 'Link',
+			'options': 'Exit Interview',
+			'width': 150
+		},
+		{
+			'label': _('Interview Status'),
+			'fieldname': 'interview_status',
+			'fieldtype': 'Data',
+			'width': 130
+		},
+		{
+			'label': _('Final Decision'),
+			'fieldname': 'employee_status',
+			'fieldtype': 'Data',
+			'width': 150
+		},
+		{
+			'label': _('Full and Final Statement'),
+			'fieldname': 'full_and_final_statement',
+			'fieldtype': 'Link',
+			'options': 'Full and Final Statement',
+			'width': 180
+		},
+		{
+			'label': _('Department'),
+			'fieldname': 'department',
+			'fieldtype': 'Link',
+			'options': 'Department',
+			'width': 120
+		},
+		{
+			'label': _('Designation'),
+			'fieldname': 'designation',
+			'fieldtype': 'Link',
+			'options': 'Designation',
+			'width': 120
+		},
+		{
+			'label': _('Reports To'),
+			'fieldname': 'reports_to',
+			'fieldtype': 'Link',
+			'options': 'Employee',
+			'width': 120
+		}
+	]
+
+def get_data(filters):
+	employee = frappe.qb.DocType('Employee')
+	interview = frappe.qb.DocType('Exit Interview')
+	fnf = frappe.qb.DocType('Full and Final Statement')
+
+	query = (
+		frappe.qb.from_(employee)
+			.left_join(interview).on(interview.employee == employee.name)
+			.left_join(fnf).on(fnf.employee == employee.name)
+			.select(
+				employee.name.as_('employee'), employee.employee_name.as_('employee_name'),
+				employee.date_of_joining.as_('date_of_joining'), employee.relieving_date.as_('relieving_date'),
+				employee.department.as_('department'), employee.designation.as_('designation'),
+				employee.reports_to.as_('reports_to'), interview.name.as_('exit_interview'),
+				interview.status.as_('interview_status'), interview.employee_status.as_('employee_status'),
+				interview.reference_document_name.as_('questionnaire'), fnf.name.as_('full_and_final_statement'))
+			.distinct()
+			.orderby(employee.relieving_date, order=Order.asc)
+			.where(
+				((employee.relieving_date.isnotnull()) | (employee.relieving_date != ''))
+				& ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2)))
+				& ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2)))
+			)
+	)
+
+	query = get_conditions(filters, query, employee, interview, fnf)
+	result = query.run(as_dict=True)
+
+	return result
+
+
+def get_conditions(filters, query, employee, interview, fnf):
+	if filters.get('from_date') and filters.get('to_date'):
+		query = query.where(employee.relieving_date[getdate(filters.get('from_date')):getdate(filters.get('to_date'))])
+
+	elif filters.get('from_date'):
+		query = query.where(employee.relieving_date >= filters.get('from_date'))
+
+	elif filters.get('to_date'):
+		query = query.where(employee.relieving_date <= filters.get('to_date'))
+
+	if filters.get('company'):
+		query = query.where(employee.company == filters.get('company'))
+
+	if filters.get('department'):
+		query = query.where(employee.department == filters.get('department'))
+
+	if filters.get('designation'):
+		query = query.where(employee.designation == filters.get('designation'))
+
+	if filters.get('employee'):
+		query = query.where(employee.name == filters.get('employee'))
+
+	if filters.get('reports_to'):
+		query = query.where(employee.reports_to == filters.get('reports_to'))
+
+	if filters.get('interview_status'):
+		query = query.where(interview.status == filters.get('interview_status'))
+
+	if filters.get('final_decision'):
+		query = query.where(interview.employee_status == filters.get('final_decision'))
+
+	if filters.get('exit_interview_pending'):
+		query = query.where((interview.name == '') | (interview.name.isnull()))
+
+	if filters.get('questionnaire_pending'):
+		query = query.where((interview.reference_document_name == '') | (interview.reference_document_name.isnull()))
+
+	if filters.get('fnf_pending'):
+		query = query.where((fnf.name == '') | (fnf.name.isnull()))
+
+	return query
+
+
+def get_chart_data(data):
+	if not data:
+		return None
+
+	retained = 0
+	exit_confirmed = 0
+	pending = 0
+
+	for entry in data:
+		if entry.employee_status == 'Employee Retained':
+			retained += 1
+		elif entry.employee_status == 'Exit Confirmed':
+			exit_confirmed += 1
+		else:
+			pending += 1
+
+	chart = {
+		'data': {
+			'labels': [_('Retained'), _('Exit Confirmed'), _('Decision Pending')],
+			'datasets': [{'name': _('Employee Status'), 'values': [retained, exit_confirmed, pending]}]
+		},
+		'type': 'donut',
+		'colors': ['green', 'red', 'blue'],
+	}
+
+	return chart
+
+
+def get_report_summary(data):
+	if not data:
+		return None
+
+	total_resignations = len(data)
+	interviews_pending = len([entry.name for entry in data if not entry.exit_interview])
+	fnf_pending = len([entry.name for entry in data if not entry.full_and_final_statement])
+	questionnaires_pending = len([entry.name for entry in data if not entry.questionnaire])
+
+	return [
+		{
+			'value': total_resignations,
+			'label': _('Total Resignations'),
+			'indicator': 'Red' if total_resignations > 0 else 'Green',
+			'datatype': 'Int',
+		},
+		{
+			'value': interviews_pending,
+			'label': _('Pending Interviews'),
+			'indicator': 'Blue' if interviews_pending > 0 else 'Green',
+			'datatype': 'Int',
+		},
+		{
+			'value': fnf_pending,
+			'label': _('Pending FnF'),
+			'indicator': 'Blue' if fnf_pending > 0 else 'Green',
+			'datatype': 'Int',
+		},
+		{
+			'value': questionnaires_pending,
+			'label': _('Pending Questionnaires'),
+			'indicator': 'Blue' if questionnaires_pending > 0 else 'Green',
+			'datatype': 'Int'
+		},
+	]
+
diff --git a/erpnext/hr/report/employee_exits/test_employee_exits.py b/erpnext/hr/report/employee_exits/test_employee_exits.py
new file mode 100644
index 0000000..d7e95a6
--- /dev/null
+++ b/erpnext/hr/report/employee_exits/test_employee_exits.py
@@ -0,0 +1,242 @@
+import unittest
+
+import frappe
+from frappe.utils import add_days, getdate
+
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.exit_interview.test_exit_interview import create_exit_interview
+from erpnext.hr.doctype.full_and_final_statement.test_full_and_final_statement import (
+	create_full_and_final_statement,
+)
+from erpnext.hr.report.employee_exits.employee_exits import execute
+
+
+class TestEmployeeExits(unittest.TestCase):
+	@classmethod
+	def setUpClass(cls):
+		create_company()
+		frappe.db.sql("delete from `tabEmployee` where company='Test Company'")
+		frappe.db.sql("delete from `tabFull and Final Statement` where company='Test Company'")
+		frappe.db.sql("delete from `tabExit Interview` where company='Test Company'")
+
+		cls.create_records()
+
+	@classmethod
+	def tearDownClass(cls):
+		frappe.db.rollback()
+
+	@classmethod
+	def create_records(cls):
+		cls.emp1 = make_employee(
+			'employeeexit1@example.com',
+			company='Test Company',
+			date_of_joining=getdate('01-10-2021'),
+			relieving_date=add_days(getdate(), 14),
+			designation='Accountant'
+		)
+		cls.emp2 = make_employee(
+			'employeeexit2@example.com',
+			company='Test Company',
+			date_of_joining=getdate('01-12-2021'),
+			relieving_date=add_days(getdate(), 15),
+			designation='Accountant'
+		)
+
+		cls.emp3 = make_employee(
+			'employeeexit3@example.com',
+			company='Test Company',
+			date_of_joining=getdate('02-12-2021'),
+			relieving_date=add_days(getdate(), 29),
+			designation='Engineer'
+		)
+		cls.emp4 = make_employee(
+			'employeeexit4@example.com',
+			company='Test Company',
+			date_of_joining=getdate('01-12-2021'),
+			relieving_date=add_days(getdate(), 30),
+			designation='Engineer'
+		)
+
+		# exit interview for 3 employees only
+		cls.interview1 = create_exit_interview(cls.emp1)
+		cls.interview2 = create_exit_interview(cls.emp2)
+		cls.interview3 = create_exit_interview(cls.emp3)
+
+		# create fnf for some records
+		cls.fnf1 = create_full_and_final_statement(cls.emp1)
+		cls.fnf2 = create_full_and_final_statement(cls.emp2)
+
+		# link questionnaire for a few records
+		# setting employee doctype as reference instead of creating a questionnaire
+		# since this is just for a test
+		frappe.db.set_value('Exit Interview', cls.interview1.name, {
+			'ref_doctype': 'Employee',
+			'reference_document_name': cls.emp1
+		})
+
+		frappe.db.set_value('Exit Interview', cls.interview2.name, {
+			'ref_doctype': 'Employee',
+			'reference_document_name': cls.emp2
+		})
+
+		frappe.db.set_value('Exit Interview', cls.interview3.name, {
+			'ref_doctype': 'Employee',
+			'reference_document_name': cls.emp3
+		})
+
+
+	def test_employee_exits_summary(self):
+		filters = {
+			'company': 'Test Company',
+			'from_date': getdate(),
+			'to_date': add_days(getdate(), 15),
+			'designation': 'Accountant'
+		}
+
+		report = execute(filters)
+
+		employee1 = frappe.get_doc('Employee', self.emp1)
+		employee2 = frappe.get_doc('Employee', self.emp2)
+		expected_data = [
+			{
+				'employee': employee1.name,
+				'employee_name': employee1.employee_name,
+				'date_of_joining': employee1.date_of_joining,
+				'relieving_date': employee1.relieving_date,
+				'department': employee1.department,
+				'designation': employee1.designation,
+				'reports_to': None,
+				'exit_interview': self.interview1.name,
+				'interview_status': self.interview1.status,
+				'employee_status': '',
+				'questionnaire': employee1.name,
+				'full_and_final_statement': self.fnf1.name
+			},
+			{
+				'employee': employee2.name,
+				'employee_name': employee2.employee_name,
+				'date_of_joining': employee2.date_of_joining,
+				'relieving_date': employee2.relieving_date,
+				'department': employee2.department,
+				'designation': employee2.designation,
+				'reports_to': None,
+				'exit_interview': self.interview2.name,
+				'interview_status': self.interview2.status,
+				'employee_status': '',
+				'questionnaire': employee2.name,
+				'full_and_final_statement': self.fnf2.name
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+
+
+	def test_pending_exit_interviews_summary(self):
+		filters = {
+			'company': 'Test Company',
+			'from_date': getdate(),
+			'to_date': add_days(getdate(), 30),
+			'exit_interview_pending': 1
+		}
+
+		report = execute(filters)
+
+		employee4 = frappe.get_doc('Employee', self.emp4)
+		expected_data = [{
+			'employee': employee4.name,
+			'employee_name': employee4.employee_name,
+			'date_of_joining': employee4.date_of_joining,
+			'relieving_date': employee4.relieving_date,
+			'department': employee4.department,
+			'designation': employee4.designation,
+			'reports_to': None,
+			'exit_interview': None,
+			'interview_status': None,
+			'employee_status': None,
+			'questionnaire': None,
+			'full_and_final_statement': None
+		}]
+
+		self.assertEqual(expected_data, report[1]) # rows
+
+	def test_pending_exit_questionnaire_summary(self):
+		filters = {
+			'company': 'Test Company',
+			'from_date': getdate(),
+			'to_date': add_days(getdate(), 30),
+			'questionnaire_pending': 1
+		}
+
+		report = execute(filters)
+
+		employee4 = frappe.get_doc('Employee', self.emp4)
+		expected_data = [{
+			'employee': employee4.name,
+			'employee_name': employee4.employee_name,
+			'date_of_joining': employee4.date_of_joining,
+			'relieving_date': employee4.relieving_date,
+			'department': employee4.department,
+			'designation': employee4.designation,
+			'reports_to': None,
+			'exit_interview': None,
+			'interview_status': None,
+			'employee_status': None,
+			'questionnaire': None,
+			'full_and_final_statement': None
+		}]
+
+		self.assertEqual(expected_data, report[1]) # rows
+
+
+	def test_pending_fnf_summary(self):
+		filters = {
+			'company': 'Test Company',
+			'fnf_pending': 1
+		}
+
+		report = execute(filters)
+
+		employee3 = frappe.get_doc('Employee', self.emp3)
+		employee4 = frappe.get_doc('Employee', self.emp4)
+		expected_data = [
+			{
+				'employee': employee3.name,
+				'employee_name': employee3.employee_name,
+				'date_of_joining': employee3.date_of_joining,
+				'relieving_date': employee3.relieving_date,
+				'department': employee3.department,
+				'designation': employee3.designation,
+				'reports_to': None,
+				'exit_interview': self.interview3.name,
+				'interview_status': self.interview3.status,
+				'employee_status': '',
+				'questionnaire': employee3.name,
+				'full_and_final_statement': None
+			},
+			{
+				'employee': employee4.name,
+				'employee_name': employee4.employee_name,
+				'date_of_joining': employee4.date_of_joining,
+				'relieving_date': employee4.relieving_date,
+				'department': employee4.department,
+				'designation': employee4.designation,
+				'reports_to': None,
+				'exit_interview': None,
+				'interview_status': None,
+				'employee_status': None,
+				'questionnaire': None,
+				'full_and_final_statement': None
+			}
+		]
+
+		self.assertEqual(expected_data, report[1]) # rows
+
+
+def create_company():
+	if not frappe.db.exists('Company', 'Test Company'):
+		frappe.get_doc({
+			'doctype': 'Company',
+			'company_name': 'Test Company',
+			'default_currency': 'INR',
+			'country': 'India'
+		}).insert()
\ No newline at end of file
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index 7408d63..85e641c 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -5,7 +5,7 @@
    "label": "Outgoing Salary"
   }
  ],
- "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Human Resource\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Employee\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Leave Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Job Applicant\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee Lifecycle\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Shift Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Leaves\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Expense Claims\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fleet Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Recruitment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loans\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Training\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Performance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
+ "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Human Resource\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Outgoing Salary\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Employee\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leave Application\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Attendance\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Applicant\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Monthly Attendance Sheet\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports &amp; Masters\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee Lifecycle\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Employee Exit\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Shift Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Leaves\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Attendance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Expense Claims\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loans\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Recruitment\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Performance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Fleet Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Training\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
  "creation": "2020-03-02 15:48:58.322521",
  "docstatus": 0,
  "doctype": "Workspace",
@@ -16,14 +16,6 @@
  "label": "HR",
  "links": [
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Employee",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -112,14 +104,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Employee Lifecycle",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "Job Applicant",
    "hidden": 0,
    "is_query_report": 0,
@@ -228,14 +212,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Shift Management",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -269,14 +245,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Leaves",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -387,14 +355,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Attendance",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "Employee",
    "hidden": 0,
    "is_query_report": 0,
@@ -450,14 +410,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Expense Claims",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "Employee",
    "hidden": 0,
    "is_query_report": 0,
@@ -490,14 +442,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Settings",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -533,14 +477,6 @@
   {
    "hidden": 0,
    "is_query_report": 0,
-   "label": "Fleet Management",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
-   "hidden": 0,
-   "is_query_report": 0,
    "label": "Driver",
    "link_count": 0,
    "link_to": "Driver",
@@ -582,14 +518,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Recruitment",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "",
    "hidden": 0,
    "is_query_report": 0,
@@ -809,14 +737,6 @@
    "type": "Link"
   },
   {
-   "hidden": 0,
-   "is_query_report": 0,
-   "label": "Key Reports",
-   "link_count": 0,
-   "onboard": 0,
-   "type": "Card Break"
-  },
-  {
    "dependencies": "Attendance",
    "hidden": 0,
    "is_query_report": 1,
@@ -933,9 +853,796 @@
    "link_type": "Report",
    "onboard": 0,
    "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Lifecycle",
+   "link_count": 7,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "Job Applicant",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Onboarding",
+   "link_count": 0,
+   "link_to": "Employee Onboarding",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Skill Map",
+   "link_count": 0,
+   "link_to": "Employee Skill Map",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Promotion",
+   "link_count": 0,
+   "link_to": "Employee Promotion",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Transfer",
+   "link_count": 0,
+   "link_to": "Employee Transfer",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Grievance Type",
+   "link_count": 0,
+   "link_to": "Grievance Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Grievance",
+   "link_count": 0,
+   "link_to": "Employee Grievance",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Onboarding Template",
+   "link_count": 0,
+   "link_to": "Employee Onboarding Template",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Exit",
+   "link_count": 4,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Separation Template",
+   "link_count": 0,
+   "link_to": "Employee Separation Template",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Separation",
+   "link_count": 0,
+   "link_to": "Employee Separation",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Full and Final Statement",
+   "link_count": 0,
+   "link_to": "Full and Final Statement",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Exit Interview",
+   "link_count": 0,
+   "link_to": "Exit Interview",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee",
+   "link_count": 8,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee",
+   "link_count": 0,
+   "link_to": "Employee",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employment Type",
+   "link_count": 0,
+   "link_to": "Employment Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Branch",
+   "link_count": 0,
+   "link_to": "Branch",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Department",
+   "link_count": 0,
+   "link_to": "Department",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Designation",
+   "link_count": 0,
+   "link_to": "Designation",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Grade",
+   "link_count": 0,
+   "link_to": "Employee Grade",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Group",
+   "link_count": 0,
+   "link_to": "Employee Group",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Health Insurance",
+   "link_count": 0,
+   "link_to": "Employee Health Insurance",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Key Reports",
+   "link_count": 7,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "Attendance",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Monthly Attendance Sheet",
+   "link_count": 0,
+   "link_to": "Monthly Attendance Sheet",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Staffing Plan",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Recruitment Analytics",
+   "link_count": 0,
+   "link_to": "Recruitment Analytics",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Employee Analytics",
+   "link_count": 0,
+   "link_to": "Employee Analytics",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Employee Leave Balance",
+   "link_count": 0,
+   "link_to": "Employee Leave Balance",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Employee Leave Balance Summary",
+   "link_count": 0,
+   "link_to": "Employee Leave Balance Summary",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee Advance",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Employee Advance Summary",
+   "link_count": 0,
+   "link_to": "Employee Advance Summary",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Exits",
+   "link_count": 0,
+   "link_to": "Employee Exits",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Recruitment",
+   "link_count": 11,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Job Opening",
+   "link_count": 0,
+   "link_to": "Job Opening",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Referral",
+   "link_count": 0,
+   "link_to": "Employee Referral",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Job Applicant",
+   "link_count": 0,
+   "link_to": "Job Applicant",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Job Offer",
+   "link_count": 0,
+   "link_to": "Job Offer",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Staffing Plan",
+   "link_count": 0,
+   "link_to": "Staffing Plan",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Appointment Letter",
+   "link_count": 0,
+   "link_to": "Appointment Letter",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Appointment Letter Template",
+   "link_count": 0,
+   "link_to": "Appointment Letter Template",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Interview Type",
+   "link_count": 0,
+   "link_to": "Interview Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Interview Round",
+   "link_count": 0,
+   "link_to": "Interview Round",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Interview",
+   "link_count": 0,
+   "link_to": "Interview",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Interview Feedback",
+   "link_count": 0,
+   "link_to": "Interview Feedback",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Fleet Management",
+   "link_count": 4,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Driver",
+   "link_count": 0,
+   "link_to": "Driver",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Vehicle",
+   "link_count": 0,
+   "link_to": "Vehicle",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Vehicle Log",
+   "link_count": 0,
+   "link_to": "Vehicle Log",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Vehicle",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Vehicle Expenses",
+   "link_count": 0,
+   "link_to": "Vehicle Expenses",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Settings",
+   "link_count": 3,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "HR Settings",
+   "link_count": 0,
+   "link_to": "HR Settings",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Daily Work Summary Group",
+   "link_count": 0,
+   "link_to": "Daily Work Summary Group",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Team Updates",
+   "link_count": 0,
+   "link_to": "team-updates",
+   "link_type": "Page",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Expense Claims",
+   "link_count": 3,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Expense Claim",
+   "link_count": 0,
+   "link_to": "Expense Claim",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Advance",
+   "link_count": 0,
+   "link_to": "Employee Advance",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Travel Request",
+   "link_count": 0,
+   "link_to": "Travel Request",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Attendance",
+   "link_count": 5,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Attendance Tool",
+   "link_count": 0,
+   "link_to": "Employee Attendance Tool",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Attendance",
+   "link_count": 0,
+   "link_to": "Attendance",
+   "link_type": "DocType",
+   "onboard": 1,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Attendance Request",
+   "link_count": 0,
+   "link_to": "Attendance Request",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Upload Attendance",
+   "link_count": 0,
+   "link_to": "Upload Attendance",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Employee Checkin",
+   "link_count": 0,
+   "link_to": "Employee Checkin",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leaves",
+   "link_count": 10,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Holiday List",
+   "link_count": 0,
+   "link_to": "Holiday List",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Type",
+   "link_count": 0,
+   "link_to": "Leave Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Period",
+   "link_count": 0,
+   "link_to": "Leave Period",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Leave Type",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Policy",
+   "link_count": 0,
+   "link_to": "Leave Policy",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Leave Policy",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Policy Assignment",
+   "link_count": 0,
+   "link_to": "Leave Policy Assignment",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Application",
+   "link_count": 0,
+   "link_to": "Leave Application",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Allocation",
+   "link_count": 0,
+   "link_to": "Leave Allocation",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Encashment",
+   "link_count": 0,
+   "link_to": "Leave Encashment",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Leave Block List",
+   "link_count": 0,
+   "link_to": "Leave Block List",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "Employee",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Compensatory Leave Request",
+   "link_count": 0,
+   "link_to": "Compensatory Leave Request",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Shift Management",
+   "link_count": 3,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Shift Type",
+   "link_count": 0,
+   "link_to": "Shift Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Shift Request",
+   "link_count": 0,
+   "link_to": "Shift Request",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Shift Assignment",
+   "link_count": 0,
+   "link_to": "Shift Assignment",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
   }
  ],
- "modified": "2021-08-31 12:18:59.842919",
+ "modified": "2021-12-05 22:05:13.004462",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
index acaa895..2c48872 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py
@@ -4,10 +4,17 @@
 def get_data():
 	return {
 		'fieldname': 'job_card',
+		'non_standard_fieldnames': {
+			'Quality Inspection': 'reference_name'
+		},
 		'transactions': [
 			{
 				'label': _('Transactions'),
 				'items': ['Material Request', 'Stock Entry']
+			},
+			{
+				'label': _('Reference'),
+				'items': ['Quality Inspection']
 			}
 		]
 	}
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index f590d68..1e74b6d 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -29,6 +29,9 @@
 		self.warehouse = '_Test Warehouse 2 - _TC'
 		self.item = '_Test Item'
 
+	def tearDown(self):
+		frappe.db.rollback()
+
 	def check_planned_qty(self):
 
 		planned0 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index ee9060b..0687b42 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -313,4 +313,8 @@
 erpnext.patches.v13_0.create_pan_field_for_india #2
 erpnext.patches.v14_0.delete_hub_doctypes
 erpnext.patches.v13_0.create_ksa_vat_custom_fields
+erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
 erpnext.patches.v14_0.migrate_crm_settings
+erpnext.patches.v13_0.rename_ksa_qr_field
+erpnext.patches.v13_0.disable_ksa_print_format_for_others
+erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
new file mode 100644
index 0000000..c815b3b
--- /dev/null
+++ b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2020, Wahni Green Technologies and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+	if company:
+		return
+
+	if frappe.db.exists('DocType', 'Print Format'):
+		frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
+		frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
+		for d in ('KSA VAT Invoice', 'KSA POS Invoice'):
+			frappe.db.set_value("Print Format", d, "disabled", 1)
diff --git a/erpnext/patches/v13_0/rename_ksa_qr_field.py b/erpnext/patches/v13_0/rename_ksa_qr_field.py
new file mode 100644
index 0000000..0bb86e0
--- /dev/null
+++ b/erpnext/patches/v13_0/rename_ksa_qr_field.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2020, Wahni Green Technologies and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
+	if not company:
+		return
+
+	if frappe.db.exists('DocType', 'Sales Invoice'):
+		frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
+		if frappe.db.has_column('Sales Invoice', 'qr_code'):
+			rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
diff --git a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
new file mode 100644
index 0000000..8b1752b
--- /dev/null
+++ b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py
@@ -0,0 +1,27 @@
+import os
+
+import frappe
+from frappe import _
+
+
+def execute():
+	frappe.reload_doc("email", "doctype", "email_template")
+	frappe.reload_doc("hr", "doctype", "hr_settings")
+
+	template = frappe.db.exists("Email Template", _("Exit Questionnaire Notification"))
+	if not template:
+		base_path = frappe.get_app_path("erpnext", "hr", "doctype")
+		response = frappe.read_file(os.path.join(base_path, "exit_interview/exit_questionnaire_notification_template.html"))
+
+		template = frappe.get_doc({
+			"doctype": "Email Template",
+			"name": _("Exit Questionnaire Notification"),
+			"response": response,
+			"subject": _("Exit Questionnaire Notification"),
+			"owner": frappe.session.user,
+		}).insert(ignore_permissions=True)
+		template = template.name
+
+	hr_settings = frappe.get_doc("HR Settings")
+	hr_settings.exit_questionnaire_notification_template = template
+	hr_settings.save()
diff --git a/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py b/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py
new file mode 100644
index 0000000..1cc5f38
--- /dev/null
+++ b/erpnext/patches/v14_0/rename_ongoing_status_in_sla_documents.py
@@ -0,0 +1,27 @@
+import frappe
+
+
+def execute():
+	active_sla_documents = [sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])]
+
+	for doctype in active_sla_documents:
+		doctype = frappe.qb.DocType(doctype)
+		try:
+			frappe.qb.update(
+				doctype
+			).set(
+				doctype.agreement_status, 'First Response Due'
+			).where(
+				doctype.first_responded_on.isnull()
+			).run()
+
+			frappe.qb.update(
+				doctype
+			).set(
+				doctype.agreement_status, 'Resolution Due'
+			).where(
+				doctype.agreement_status == 'Ongoing'
+			).run()
+
+		except Exception:
+			frappe.log_error(title='Failed to Patch SLA Status')
\ No newline at end of file
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index f0facdd..6654048 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -84,6 +84,10 @@
 		});
 	},
 
+	route_to_pending_reposts: (args) => {
+		frappe.set_route('List', 'Repost Item Valuation', args);
+	},
+
 	proceed_save_with_reminders_frequency_change: () => {
 		frappe.ui.hide_open_dialog();
 
@@ -831,7 +835,7 @@
 
 					refresh: function(frm) {
 						if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement
-							&& frm.doc.agreement_status === 'Ongoing') {
+							&& ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) {
 							frappe.call({
 								'method': 'frappe.client.get',
 								args: {
@@ -884,8 +888,8 @@
 function set_time_to_resolve_and_response(frm, apply_sla_for_resolution) {
 	frm.dashboard.clear_headline();
 
-	let time_to_respond = get_status(frm.doc.response_by_variance);
-	if (!frm.doc.first_responded_on && frm.doc.agreement_status === 'Ongoing') {
+	let time_to_respond = get_status(frm.doc.response_by);
+	if (!frm.doc.first_responded_on) {
 		time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_status);
 	}
 
@@ -899,8 +903,8 @@
 
 
 	if (apply_sla_for_resolution) {
-		let time_to_resolve = get_status(frm.doc.resolution_by_variance);
-		if (!frm.doc.resolution_date && frm.doc.agreement_status === 'Ongoing') {
+		let time_to_resolve = get_status(frm.doc.resolution_by);
+		if (!frm.doc.resolution_date) {
 			time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_status);
 		}
 
@@ -924,8 +928,9 @@
 	return {'diff_display': diff_display, 'indicator': indicator};
 }
 
-function get_status(variance) {
-	if (variance > 0) {
+function get_status(timestamp) {
+	const time_left = moment(timestamp).diff(moment());
+	if (time_left >= 0) {
 		return {'diff_display': 'Fulfilled', 'indicator': 'green'};
 	} else {
 		return {'diff_display': 'Failed', 'indicator': 'red'};
diff --git a/erpnext/regional/print_format/ksa_pos_invoice/__init__.py b/erpnext/regional/print_format/ksa_pos_invoice/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/regional/print_format/ksa_pos_invoice/__init__.py
diff --git a/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json b/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
new file mode 100644
index 0000000..c2a3092
--- /dev/null
+++ b/erpnext/regional/print_format/ksa_pos_invoice/ksa_pos_invoice.json
@@ -0,0 +1,32 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2021-12-07 13:25:05.424827",
+ "css": "",
+ "custom_format": 1,
+ "default_print_language": "en",
+ "disabled": 1,
+ "doc_type": "POS Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font_size": 0,
+ "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tline-height: 150%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n{% if letter_head %}\n    {{ letter_head }}\n{% endif %}\n\n<p class=\"text-center\" style=\"margin-bottom: 1rem\">\n\t{{ doc.company }}<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n\t<img src={{doc.ksa_einv_qr}}>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Cashier\") }}:</b> {{ doc.owner }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Time\") }}:</b> {{  doc.get_formatted(\"posting_time\") }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"35%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"SR.No\") }}:</b><br>\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"net_amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t    {% if '%' in row.description %}\n\t\t\t\t\t    {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t    {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t<b>{{ _(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.change_amount -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 60%\">\n\t\t\t\t\t<b>{{ _(\"Change Amount\") }}</b>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
+ "idx": 0,
+ "line_breaks": 0,
+ "margin_bottom": 0.0,
+ "margin_left": 0.0,
+ "margin_right": 0.0,
+ "margin_top": 0.0,
+ "modified": "2021-12-08 10:25:01.930885",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA POS Invoice",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "print_format_builder": 0,
+ "print_format_builder_beta": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
index 8e9a728..6b64d47 100644
--- a/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
+++ b/erpnext/regional/print_format/ksa_vat_invoice/ksa_vat_invoice.json
@@ -5,19 +5,19 @@
  "css": ".qr-code{\n    float:right;\n}\n\n.invoice-heading {\n  margin: 0;\n}\n\n.ksa-invoice-table {\n  border: 1px solid #888a8e;\n  border-collapse: collapse;\n  width: 100%;\n  margin: 20px 0;\n  font-size: 16px;\n}\n\n.ksa-invoice-table.two-columns td:nth-child(2) {\n  direction: rtl;\n}\n\n.ksa-invoice-table th {\n  border: 1px solid #888a8e;\n  max-width: 50%;\n  padding: 8px;\n}\n\n.ksa-invoice-table td {\n  padding: 5px;\n  border: 1px solid #888a8e;\n  max-width: 50%;\n}\n\n.ksa-invoice-table thead,\n.ksa-invoice-table tfoot {\n  text-transform: uppercase;\n}\n\n.qr-rtl {\n  direction: rtl;\n}\n\n.qr-flex{\n    display: flex;\n    justify-content: space-between;\n}",
  "custom_format": 1,
  "default_print_language": "en",
- "disabled": 0,
+ "disabled": 1,
  "doc_type": "Sales Invoice",
  "docstatus": 0,
  "doctype": "Print Format",
  "font_size": 14,
- "html": "<div class=\"ksa-vat-format\">\n    <div class=\"qr-flex\">\n        <div style=\"qr-flex: 1\">\n            <h2 class=\"invoice-heading\">TAX INVOICE</h2>\n            <h2 class=\"invoice-heading\">\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629</h2>\n        </div>\n        \n        <img class=\"qr-code\" src={{doc.qr_code}}>\n    </div>\n    {% set company = frappe.get_doc(\"Company\", doc.company)%}\n    {% if (doc.company_address) %}\n        {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n    {% endif %}\n    \n    {% if(doc.customer_address) %}\n        {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n    {% endif %}\n    \n    {% if(doc.shipping_address_name) %}\n        {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n    {% endif %}  \n        \n    <table class=\"ksa-invoice-table two-columns\">\n      <thead>\n        <tr>\n          <th>{{ company.name }}</th>\n          <th style=\"text-align: right;\">{{ company.company_name_in_arabic }}</th>\n        </tr>\n      </thead>\n\n      <tbody>\n        <!-- Invoice Info -->\n        <tr>\n          <td>Invoice#: {{doc.name}}</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}</td>\n        </tr>\n        <tr>\n          <td>Invoice Date: {{doc.posting_date}}</td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}</td>\n        </tr>\n        <tr>\n          <td>Date of Supply:{{doc.posting_date}}</td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}</td>\n        </tr>\n        \n        <!--Supplier Info -->\n        <tr>\n          <td>Supplier:</td>\n          <td>\u0627\u0644\u0645\u0648\u0631\u062f:</td>\n        </tr>\n\t\t{% if (company.tax_id) %}\n        <tr>\n          <td>Supplier Tax Identification Number:</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:</td>\n        </tr>\n        <tr>\n          <td>{{ company.tax_id }}</td>\n          <td>{{ company.tax_id }}</td>\n        </tr>\n        {% endif %}\n        <tr>\n          <td>{{ company.name }}</td>\n          <td>{{ company.company_name_in_arabic }} </td>\n        </tr>\n        \n        \n        {% if(supplier_address_doc) %}\n        <tr>\n          <td>{{ supplier_address_doc.address_line1}} </td>\n          <td>{{ supplier_address_doc.address_in_arabic}} </td>\n        </tr>\n        <tr>\n          <td>Phone: {{ supplier_address_doc.phone }}</td>\n          <td>\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}</td>\n        </tr>\n        <tr>\n          <td>Email: {{ supplier_address_doc.email_id }}</td>\n          <td>\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}</td>\n        </tr>\n        {% endif %}\n        \n        <!-- Customer Info -->\n        <tr>\n          <td>CUSTOMER:</td>\n          <td>\u0639\u0645\u064a\u0644:</td>\n        </tr>\n\t\t{% set customer_tax_id = frappe.db.get_value('Customer', doc.customer, 'tax_id') %}\n\t\t{% if customer_tax_id %}\n        <tr>\n          <td>Customer Tax Identification Number:</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0639\u0645\u064a\u0644:</td>\n        </tr>\n        <tr>\n          <td>{{ customer_tax_id }}</td>\n          <td>{{ customer_tax_id }}</td>\n        </tr>\n        {% endif %}\n        <tr>\n          <td> {{ doc.customer }}</td>\n          <td> {{ doc.customer_name_in_arabic }} </td>\n        </tr>\n        \n        {% if(customer_address) %}\n        <tr>\n          <td>{{ customer_address.address_line1}} </td>\n          <td>{{ customer_address.address_in_arabic}} </td>\n        </tr>\n        {% endif %}\n        \n        {% if(customer_shipping_address) %}\n        <tr>\n          <td>SHIPPING ADDRESS:</td>\n          <td>\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:</td>\n        </tr>\n        \n        <tr>\n          <td>{{ customer_shipping_address.address_line1}} </td>\n          <td>{{ customer_shipping_address.address_in_arabic}} </td>\n        </tr>\n        {% endif %}\n        \n\t\t{% if(doc.po_no) %}\n        <tr>\n          <td>OTHER INFORMATION</td>\n          <td>\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649</td>\n        </tr>\n        \n        <tr>\n          <td>Purchase Order Number: {{ doc.po_no }}</td>\n          <td>\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}</td>\n        </tr>\n        {% endif %}\n        \n        <tr>\n          <td>Payment Due Date: {{  doc.due_date}} </td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{  doc.due_date}}</td>\n        </tr>\n      </tbody>\n    </table>\n\n    <!--Dynamic Colspan for total row columns-->\n    {% set col = namespace(one = 2, two = 1) %}\n    {% set length = doc.taxes | length %}\n    {% set length = length / 2 | round %}\n    {% set col.one = col.one + length %}\n    {% set col.two = col.two + length %}\n  \n    {%- if(doc.taxes | length % 2 > 0 ) -%}\n      {% set col.two = col.two + 1 %}\n    {% endif %}\n    \n    <!-- Items -->\n    {% set total = namespace(amount = 0) %}\n    <table class=\"ksa-invoice-table\">\n      <thead>\n        <tr>\n          <th>Nature of goods or services <br />\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a</th>\n          <th>\n            Unit price <br />\n            \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n          </th>\n          <th>\n            Quantity <br />\n            \u0627\u0644\u0643\u0645\u064a\u0629\n          </th>\n          <th>\n            Taxable Amount <br />\n            \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n          </th>\n          \n          {% for row in doc.taxes %}\n            <th style=\"min-width: 130px\">{{row.description}}</th>\n          {% endfor %}\n          \n          <th>\n            Total <br />\n            \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n          </th>\n        </tr>\n      </thead>\n      <tbody>\n        {%- for item in doc.items -%}\n        {% set total.amount = item.amount %}\n        <tr>\n          <td>{{ item.item_code or item.item_name }}</td>\n          <td>{{ item.get_formatted(\"rate\") }}</td>\n          <td>{{ item.qty }}</td>\n          <td>{{ item.get_formatted(\"amount\") }}</td>\n           {% for row in doc.taxes %}\n                {% set data_object = json.loads(row.item_wise_tax_detail) %}\n                {% set key = item.item_code or item.item_name %}\n                {% set tax_amount = frappe.utils.flt(data_object[key][1]/doc.conversion_rate, row.precision('tax_amount')) %}\n                <td>\n                   <div class=\"qr-flex\">\n                    {%- if(data_object[key][0])-%}\n                    <span>{{ frappe.format(data_object[key][0], {'fieldtype': 'Percent'}) }}</span>\n                    {%- endif -%}\n                    <span>\n                    {%- if(data_object[key][1])-%}\n                        {{ frappe.format_value(tax_amount, currency=doc.currency) }}</span>\n                        {% set total.amount = total.amount + tax_amount %}\n                    {%- endif -%}\n                    </div>\n                </td>\n            {% endfor %}\n          <td>{{  frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.currency) }}</td>\n        </tr>\n        {%- endfor -%}\n      </tbody>\n      <tfoot>\n        <tr>\n          <td>\n            {{ doc.get_formatted(\"total\") }} <br />\n            {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n          </td>\n          \n          <td colspan={{ col.one }} class=\"qr-rtl\">\n            \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n            <br />\n            \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n          </td>\n          <td colspan={{ col.two }}>\n            Total (Excluding VAT)\n            <br />\n            Total VAT\n          </td>\n          <td>\n            {{ doc.get_formatted(\"total\") }} <br />\n            {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n          </td>\n        </tr>\n        <tr>\n          <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n          <td  colspan={{  col.one }} class=\"qr-rtl\">\n              \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642</td>\n          <td  colspan={{  col.two }}>Total Amount Due</td>\n          <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n        </tr>\n      </tfoot>\n    </table>\n\n\t{%- if doc.terms -%}\n    <p>\n      {{doc.terms}}\n    </p>\n\t{%- endif -%}\n</div>\n",
+ "html": "<div class=\"ksa-vat-format\">\n    <div class=\"qr-flex\">\n        <div style=\"qr-flex: 1\">\n            <h2 class=\"invoice-heading\">TAX INVOICE</h2>\n            <h2 class=\"invoice-heading\">\u0641\u0627\u062a\u0648\u0631\u0629 \u0636\u0631\u064a\u0628\u064a\u0629</h2>\n        </div>\n        \n        <img class=\"qr-code\" src={{doc.ksa_einv_qr}}>\n    </div>\n    {% set company = frappe.get_doc(\"Company\", doc.company)%}\n    {% if (doc.company_address) %}\n        {% set supplier_address_doc = frappe.get_doc('Address', doc.company_address) %}\n    {% endif %}\n    \n    {% if(doc.customer_address) %}\n        {% set customer_address = frappe.get_doc('Address', doc.customer_address ) %}\n    {% endif %}\n    \n    {% if(doc.shipping_address_name) %}\n        {% set customer_shipping_address = frappe.get_doc('Address', doc.shipping_address_name ) %}\n    {% endif %}  \n        \n    <table class=\"ksa-invoice-table two-columns\">\n      <thead>\n        <tr>\n          <th>{{ company.name }}</th>\n          <th style=\"text-align: right;\">{{ company.company_name_in_arabic }}</th>\n        </tr>\n      </thead>\n\n      <tbody>\n        <!-- Invoice Info -->\n        <tr>\n          <td>Invoice#: {{doc.name}}</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.name}}</td>\n        </tr>\n        <tr>\n          <td>Invoice Date: {{doc.posting_date}}</td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0641\u0627\u062a\u0648\u0631\u0629: {{doc.posting_date}}</td>\n        </tr>\n        <tr>\n          <td>Date of Supply:{{doc.posting_date}}</td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u062a\u0648\u0631\u064a\u062f: {{doc.posting_date}}</td>\n        </tr>\n        \n        <!--Supplier Info -->\n        <tr>\n          <td>Supplier:</td>\n          <td>\u0627\u0644\u0645\u0648\u0631\u062f:</td>\n        </tr>\n\t\t{% if (company.tax_id) %}\n        <tr>\n          <td>Supplier Tax Identification Number:</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0645\u0648\u0631\u062f:</td>\n        </tr>\n        <tr>\n          <td>{{ company.tax_id }}</td>\n          <td>{{ company.tax_id }}</td>\n        </tr>\n        {% endif %}\n        <tr>\n          <td>{{ company.name }}</td>\n          <td>{{ company.company_name_in_arabic }} </td>\n        </tr>\n        \n        \n        {% if(supplier_address_doc) %}\n        <tr>\n          <td>{{ supplier_address_doc.address_line1}} </td>\n          <td>{{ supplier_address_doc.address_in_arabic}} </td>\n        </tr>\n        <tr>\n          <td>Phone: {{ supplier_address_doc.phone }}</td>\n          <td>\u0647\u0627\u062a\u0641: {{ supplier_address_doc.phone }}</td>\n        </tr>\n        <tr>\n          <td>Email: {{ supplier_address_doc.email_id }}</td>\n          <td>\u0628\u0631\u064a\u062f \u0627\u0644\u0643\u062a\u0631\u0648\u0646\u064a: {{ supplier_address_doc.email_id }}</td>\n        </tr>\n        {% endif %}\n        \n        <!-- Customer Info -->\n        <tr>\n          <td>CUSTOMER:</td>\n          <td>\u0639\u0645\u064a\u0644:</td>\n        </tr>\n\t\t{% set customer_tax_id = frappe.db.get_value('Customer', doc.customer, 'tax_id') %}\n\t\t{% if customer_tax_id %}\n        <tr>\n          <td>Customer Tax Identification Number:</td>\n          <td>\u0631\u0642\u0645 \u0627\u0644\u062a\u0639\u0631\u064a\u0641 \u0627\u0644\u0636\u0631\u064a\u0628\u064a \u0644\u0644\u0639\u0645\u064a\u0644:</td>\n        </tr>\n        <tr>\n          <td>{{ customer_tax_id }}</td>\n          <td>{{ customer_tax_id }}</td>\n        </tr>\n        {% endif %}\n        <tr>\n          <td> {{ doc.customer }}</td>\n          <td> {{ doc.customer_name_in_arabic }} </td>\n        </tr>\n        \n        {% if(customer_address) %}\n        <tr>\n          <td>{{ customer_address.address_line1}} </td>\n          <td>{{ customer_address.address_in_arabic}} </td>\n        </tr>\n        {% endif %}\n        \n        {% if(customer_shipping_address) %}\n        <tr>\n          <td>SHIPPING ADDRESS:</td>\n          <td>\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0634\u062d\u0646:</td>\n        </tr>\n        \n        <tr>\n          <td>{{ customer_shipping_address.address_line1}} </td>\n          <td>{{ customer_shipping_address.address_in_arabic}} </td>\n        </tr>\n        {% endif %}\n        \n\t\t{% if(doc.po_no) %}\n        <tr>\n          <td>OTHER INFORMATION</td>\n          <td>\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062e\u0631\u0649</td>\n        </tr>\n        \n        <tr>\n          <td>Purchase Order Number: {{ doc.po_no }}</td>\n          <td>\u0631\u0642\u0645 \u0623\u0645\u0631 \u0627\u0644\u0634\u0631\u0627\u0621: {{ doc.po_no }}</td>\n        </tr>\n        {% endif %}\n        \n        <tr>\n          <td>Payment Due Date: {{  doc.due_date}} </td>\n          <td>\u062a\u0627\u0631\u064a\u062e \u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0627\u0644\u062f\u0641\u0639: {{  doc.due_date}}</td>\n        </tr>\n      </tbody>\n    </table>\n\n    <!--Dynamic Colspan for total row columns-->\n    {% set col = namespace(one = 2, two = 1) %}\n    {% set length = doc.taxes | length %}\n    {% set length = length / 2 | round %}\n    {% set col.one = col.one + length %}\n    {% set col.two = col.two + length %}\n  \n    {%- if(doc.taxes | length % 2 > 0 ) -%}\n      {% set col.two = col.two + 1 %}\n    {% endif %}\n    \n    <!-- Items -->\n    {% set total = namespace(amount = 0) %}\n    <table class=\"ksa-invoice-table\">\n      <thead>\n        <tr>\n          <th>Nature of goods or services <br />\u0637\u0628\u064a\u0639\u0629 \u0627\u0644\u0633\u0644\u0639 \u0623\u0648 \u0627\u0644\u062e\u062f\u0645\u0627\u062a</th>\n          <th>\n            Unit price <br />\n            \u0633\u0639\u0631 \u0627\u0644\u0648\u062d\u062f\u0629\n          </th>\n          <th>\n            Quantity <br />\n            \u0627\u0644\u0643\u0645\u064a\u0629\n          </th>\n          <th>\n            Taxable Amount <br />\n            \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u062e\u0627\u0636\u0639 \u0644\u0644\u0636\u0631\u064a\u0628\u0629\n          </th>\n          \n          {% for row in doc.taxes %}\n            <th style=\"min-width: 130px\">{{row.description}}</th>\n          {% endfor %}\n          \n          <th>\n            Total <br />\n            \u0627\u0644\u0645\u062c\u0645\u0648\u0639\n          </th>\n        </tr>\n      </thead>\n      <tbody>\n        {%- for item in doc.items -%}\n        {% set total.amount = item.amount %}\n        <tr>\n          <td>{{ item.item_code or item.item_name }}</td>\n          <td>{{ item.get_formatted(\"rate\") }}</td>\n          <td>{{ item.qty }}</td>\n          <td>{{ item.get_formatted(\"amount\") }}</td>\n           {% for row in doc.taxes %}\n                {% set data_object = json.loads(row.item_wise_tax_detail) %}\n                {% set key = item.item_code or item.item_name %}\n                {% set tax_amount = frappe.utils.flt(data_object[key][1]/doc.conversion_rate, row.precision('tax_amount')) %}\n                <td>\n                   <div class=\"qr-flex\">\n                    {%- if(data_object[key][0])-%}\n                    <span>{{ frappe.format(data_object[key][0], {'fieldtype': 'Percent'}) }}</span>\n                    {%- endif -%}\n                    <span>\n                    {%- if(data_object[key][1])-%}\n                        {{ frappe.format_value(tax_amount, currency=doc.currency) }}</span>\n                        {% set total.amount = total.amount + tax_amount %}\n                    {%- endif -%}\n                    </div>\n                </td>\n            {% endfor %}\n          <td>{{  frappe.format_value(frappe.utils.flt(total.amount, doc.precision('total_taxes_and_charges')), currency=doc.currency) }}</td>\n        </tr>\n        {%- endfor -%}\n      </tbody>\n      <tfoot>\n        <tr>\n          <td>\n            {{ doc.get_formatted(\"total\") }} <br />\n            {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n          </td>\n          \n          <td colspan={{ col.one }} class=\"qr-rtl\">\n            \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a \u0628\u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n            <br />\n            \u0625\u062c\u0645\u0627\u0644\u064a \u0636\u0631\u064a\u0628\u0629 \u0627\u0644\u0642\u064a\u0645\u0629 \u0627\u0644\u0645\u0636\u0627\u0641\u0629\n          </td>\n          <td colspan={{ col.two }}>\n            Total (Excluding VAT)\n            <br />\n            Total VAT\n          </td>\n          <td>\n            {{ doc.get_formatted(\"total\") }} <br />\n            {{ doc.get_formatted(\"total_taxes_and_charges\") }}\n          </td>\n        </tr>\n        <tr>\n          <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n          <td  colspan={{  col.one }} class=\"qr-rtl\">\n              \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0628\u0644\u063a \u0627\u0644\u0645\u0633\u062a\u062d\u0642</td>\n          <td  colspan={{  col.two }}>Total Amount Due</td>\n          <td>{{ doc.get_formatted(\"grand_total\") }}</td>\n        </tr>\n      </tfoot>\n    </table>\n\n\t{%- if doc.terms -%}\n    <p>\n      {{doc.terms}}\n    </p>\n\t{%- endif -%}\n</div>\n",
  "idx": 0,
  "line_breaks": 0,
  "margin_bottom": 15.0,
  "margin_left": 15.0,
  "margin_right": 15.0,
  "margin_top": 15.0,
- "modified": "2021-11-29 13:47:37.870818",
+ "modified": "2021-12-07 13:43:38.018593",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "KSA VAT Invoice",
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 38a089c..2e31c03 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -3,7 +3,7 @@
 
 import frappe
 from frappe.permissions import add_permission, update_permission_property
-from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats
+from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields
 from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
@@ -13,6 +13,16 @@
 	add_permissions()
 	make_custom_fields()
 
+def add_print_formats():
+	frappe.reload_doc("regional", "print_format", "detailed_tax_invoice", force=True)
+	frappe.reload_doc("regional", "print_format", "simplified_tax_invoice", force=True)
+	frappe.reload_doc("regional", "print_format", "tax_invoice", force=True)
+	frappe.reload_doc("regional", "print_format", "ksa_vat_invoice", force=True)
+	frappe.reload_doc("regional", "print_format", "ksa_pos_invoice", force=True)
+
+	for d in ('Simplified Tax Invoice', 'Detailed Tax Invoice', 'Tax Invoice', 'KSA VAT Invoice', 'KSA POS Invoice'):
+		frappe.db.set_value("Print Format", d, "disabled", 0)
+
 def add_permissions():
 	"""Add Permissions for KSA VAT Setting."""
 	add_permission('KSA VAT Setting', 'All', 0)
@@ -33,8 +43,16 @@
 	custom_fields = {
 		'Sales Invoice': [
 			dict(
-				fieldname='qr_code',
-				label='QR Code',
+				fieldname='ksa_einv_qr',
+				label='KSA E-Invoicing QR',
+				fieldtype='Attach Image',
+				read_only=1, no_copy=1, hidden=1
+			)
+		],
+		'POS Invoice': [
+			dict(
+				fieldname='ksa_einv_qr',
+				label='KSA E-Invoicing QR',
 				fieldtype='Attach Image',
 				read_only=1, no_copy=1, hidden=1
 			)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
index 7d00d8b..a03c3f0 100644
--- a/erpnext/regional/saudi_arabia/utils.py
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -4,144 +4,146 @@
 
 import frappe
 from frappe import _
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.utils.data import add_to_date, get_time, getdate
 from pyqrcode import create as qr_create
 
 from erpnext import get_region
 
 
-def create_qr_code(doc, method):
-	"""Create QR Code after inserting Sales Inv
-	"""
-
+def create_qr_code(doc, method=None):
 	region = get_region(doc.company)
 	if region not in ['Saudi Arabia']:
 		return
 
-	# if QR Code field not present, do nothing
-	if not hasattr(doc, 'qr_code'):
-		return
+	# if QR Code field not present, create it. Invoices without QR are invalid as per law.
+	if not hasattr(doc, 'ksa_einv_qr'):
+		create_custom_fields({
+			doc.doctype: [
+				dict(
+					fieldname='ksa_einv_qr',
+					label='KSA E-Invoicing QR',
+					fieldtype='Attach Image',
+					read_only=1, no_copy=1, hidden=1
+				)
+			]
+		})
 
 	# Don't create QR Code if it already exists
-	qr_code = doc.get("qr_code")
+	qr_code = doc.get("ksa_einv_qr")
 	if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
 		return
 
-	meta = frappe.get_meta('Sales Invoice')
+	meta = frappe.get_meta(doc.doctype)
 
-	for field in meta.get_image_fields():
-		if field.fieldname == 'qr_code':
-			''' TLV conversion for
-			1. Seller's Name
-			2. VAT Number
-			3. Time Stamp
-			4. Invoice Amount
-			5. VAT Amount
-			'''
-			tlv_array = []
-			# Sellers Name
+	if "ksa_einv_qr" in [d.fieldname for d in meta.get_image_fields()]:
+		''' TLV conversion for
+		1. Seller's Name
+		2. VAT Number
+		3. Time Stamp
+		4. Invoice Amount
+		5. VAT Amount
+		'''
+		tlv_array = []
+		# Sellers Name
 
-			seller_name = frappe.db.get_value(
-				'Company',
-				doc.company,
-				'company_name_in_arabic')
+		seller_name = frappe.db.get_value(
+			'Company',
+			doc.company,
+			'company_name_in_arabic')
 
-			if not seller_name:
-				frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company))
+		if not seller_name:
+			frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company))
 
-			tag = bytes([1]).hex()
-			length = bytes([len(seller_name.encode('utf-8'))]).hex()
-			value = seller_name.encode('utf-8').hex()
-			tlv_array.append(''.join([tag, length, value]))
+		tag = bytes([1]).hex()
+		length = bytes([len(seller_name.encode('utf-8'))]).hex()
+		value = seller_name.encode('utf-8').hex()
+		tlv_array.append(''.join([tag, length, value]))
 
-			# VAT Number
-			tax_id = frappe.db.get_value('Company', doc.company, 'tax_id')
-			if not tax_id:
-				frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company))
+		# VAT Number
+		tax_id = frappe.db.get_value('Company', doc.company, 'tax_id')
+		if not tax_id:
+			frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company))
 
-			tag = bytes([2]).hex()
-			length = bytes([len(tax_id)]).hex()
-			value = tax_id.encode('utf-8').hex()
-			tlv_array.append(''.join([tag, length, value]))
+		tag = bytes([2]).hex()
+		length = bytes([len(tax_id)]).hex()
+		value = tax_id.encode('utf-8').hex()
+		tlv_array.append(''.join([tag, length, value]))
 
-			# Time Stamp
-			posting_date = getdate(doc.posting_date)
-			time = get_time(doc.posting_time)
-			seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
-			time_stamp = add_to_date(posting_date, seconds=seconds)
-			time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ')
+		# Time Stamp
+		posting_date = getdate(doc.posting_date)
+		time = get_time(doc.posting_time)
+		seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
+		time_stamp = add_to_date(posting_date, seconds=seconds)
+		time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ')
 
-			tag = bytes([3]).hex()
-			length = bytes([len(time_stamp)]).hex()
-			value = time_stamp.encode('utf-8').hex()
-			tlv_array.append(''.join([tag, length, value]))
+		tag = bytes([3]).hex()
+		length = bytes([len(time_stamp)]).hex()
+		value = time_stamp.encode('utf-8').hex()
+		tlv_array.append(''.join([tag, length, value]))
 
-			# Invoice Amount
-			invoice_amount = str(doc.grand_total)
-			tag = bytes([4]).hex()
-			length = bytes([len(invoice_amount)]).hex()
-			value = invoice_amount.encode('utf-8').hex()
-			tlv_array.append(''.join([tag, length, value]))
+		# Invoice Amount
+		invoice_amount = str(doc.grand_total)
+		tag = bytes([4]).hex()
+		length = bytes([len(invoice_amount)]).hex()
+		value = invoice_amount.encode('utf-8').hex()
+		tlv_array.append(''.join([tag, length, value]))
 
-			# VAT Amount
-			vat_amount = str(doc.total_taxes_and_charges)
+		# VAT Amount
+		vat_amount = str(doc.total_taxes_and_charges)
 
-			tag = bytes([5]).hex()
-			length = bytes([len(vat_amount)]).hex()
-			value = vat_amount.encode('utf-8').hex()
-			tlv_array.append(''.join([tag, length, value]))
+		tag = bytes([5]).hex()
+		length = bytes([len(vat_amount)]).hex()
+		value = vat_amount.encode('utf-8').hex()
+		tlv_array.append(''.join([tag, length, value]))
 
-			# Joining bytes into one
-			tlv_buff = ''.join(tlv_array)
+		# Joining bytes into one
+		tlv_buff = ''.join(tlv_array)
 
-			# base64 conversion for QR Code
-			base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
+		# base64 conversion for QR Code
+		base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
 
-			qr_image = io.BytesIO()
-			url = qr_create(base64_string, error='L')
-			url.png(qr_image, scale=2, quiet_zone=1)
+		qr_image = io.BytesIO()
+		url = qr_create(base64_string, error='L')
+		url.png(qr_image, scale=2, quiet_zone=1)
 
-			name = frappe.generate_hash(doc.name, 5)
+		name = frappe.generate_hash(doc.name, 5)
 
-			# making file
-			filename = f"QRCode-{name}.png".replace(os.path.sep, "__")
-			_file = frappe.get_doc({
-				"doctype": "File",
-				"file_name": filename,
-				"is_private": 0,
-				"content": qr_image.getvalue(),
-				"attached_to_doctype": doc.get("doctype"),
-				"attached_to_name": doc.get("name"),
-				"attached_to_field": "qr_code"
-			})
+		# making file
+		filename = f"QRCode-{name}.png".replace(os.path.sep, "__")
+		_file = frappe.get_doc({
+			"doctype": "File",
+			"file_name": filename,
+			"is_private": 0,
+			"content": qr_image.getvalue(),
+			"attached_to_doctype": doc.get("doctype"),
+			"attached_to_name": doc.get("name"),
+			"attached_to_field": "ksa_einv_qr"
+		})
 
-			_file.save()
+		_file.save()
 
-			# assigning to document
-			doc.db_set('qr_code', _file.file_url)
-			doc.notify_update()
-
-			break
+		# assigning to document
+		doc.db_set('ksa_einv_qr', _file.file_url)
+		doc.notify_update()
 
 
-def delete_qr_code_file(doc, method):
-	"""Delete QR Code on deleted sales invoice"""
-
+def delete_qr_code_file(doc, method=None):
 	region = get_region(doc.company)
 	if region not in ['Saudi Arabia']:
 		return
 
-	if hasattr(doc, 'qr_code'):
-		if doc.get('qr_code'):
+	if hasattr(doc, 'ksa_einv_qr'):
+		if doc.get('ksa_einv_qr'):
 			file_doc = frappe.get_list('File', {
-				'file_url': doc.get('qr_code')
+				'file_url': doc.get('ksa_einv_qr')
 			})
 			if len(file_doc):
 				frappe.delete_doc('File', file_doc[0].name)
 
-def delete_vat_settings_for_company(doc, method):
+def delete_vat_settings_for_company(doc, method=None):
 	if doc.country != 'Saudi Arabia':
 		return
 
-	settings_doc = frappe.get_doc('KSA VAT Setting', {'company': doc.name})
-	settings_doc.delete()
\ No newline at end of file
+	if frappe.db.exists('KSA VAT Setting', doc.name):
+		frappe.delete_doc('KSA VAT Setting', doc.name)
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index e69e28d..cc95185 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -63,6 +63,8 @@
 		if not self.billing_status: self.billing_status = 'Not Billed'
 		if not self.delivery_status: self.delivery_status = 'Not Delivered'
 
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+
 	def validate_po(self):
 		# validate p.o date v/s delivery date
 		if self.po_date and not self.skip_delivery_note:
diff --git a/erpnext/setup/setup_wizard/operations/defaults_setup.py b/erpnext/setup/setup_wizard/operations/defaults_setup.py
index e4b1fa2..ca1f57e 100644
--- a/erpnext/setup/setup_wizard/operations/defaults_setup.py
+++ b/erpnext/setup/setup_wizard/operations/defaults_setup.py
@@ -68,6 +68,8 @@
 
 	hr_settings.send_interview_feedback_reminder = 1
 	hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
+
+	hr_settings.exit_questionnaire_notification_template = _("Exit Questionnaire Notification")
 	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 98f9119..97d850b 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -278,6 +278,11 @@
 	records += [{'doctype': 'Email Template', 'name': _('Interview Feedback Reminder'), 'response': response,
 		'subject': _('Interview Feedback Reminder'), 'owner': frappe.session.user}]
 
+	response = frappe.read_file(os.path.join(base_path, 'exit_interview/exit_questionnaire_notification_template.html'))
+
+	records += [{'doctype': 'Email Template', 'name': _('Exit Questionnaire Notification'), 'response': response,
+		'subject': _('Exit Questionnaire Notification'), '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"))
 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 5268460..70d48a4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -138,6 +138,7 @@
 		self.update_current_stock()
 
 		if not self.installation_status: self.installation_status = 'Not Installed'
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
 
 	def validate_with_previous_doc(self):
 		super(DeliveryNote, self).validate_with_previous_doc({
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index d717c50..103e8d6 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -80,6 +80,9 @@
 		# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
 		# Though the creation of Material Request from a Production Plan can be rethought to fix this
 
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+
 	def set_title(self):
 		'''Set title as comma separated list of items'''
 		if not self.title:
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 762f45f..c97b306 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -118,6 +118,10 @@
 		if getdate(self.posting_date) > getdate(nowdate()):
 			throw(_("Posting Date cannot be future date"))
 
+		self.reset_default_field_value("set_warehouse", "items", "warehouse")
+		self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
+		self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+
 
 	def validate_cwip_accounts(self):
 		for item in self.get('items'):
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 01cceb1..b2ad07f 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -168,8 +168,8 @@
 	for row in riv_entries:
 		doc = frappe.get_doc('Repost Item Valuation', row.name)
 		if doc.status in ('Queued', 'In Progress'):
-			doc.deduplicate_similar_repost()
 			repost(doc)
+			doc.deduplicate_similar_repost()
 
 	riv_entries = get_repost_item_valuation_entries()
 	if riv_entries:
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index de79316..78b432d 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -4,12 +4,14 @@
 import unittest
 
 import frappe
+from frappe.utils import nowdate
 
 from erpnext.controllers.stock_controller import create_item_wise_repost_entries
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
 	in_configured_timeslot,
 )
+from erpnext.stock.utils import PendingRepostingError
 
 
 class TestRepostItemValuation(unittest.TestCase):
@@ -138,3 +140,25 @@
 		# to avoid breaking other tests accidentaly
 		riv4.set_status("Skipped")
 		riv3.set_status("Skipped")
+
+	def test_stock_freeze_validation(self):
+
+		today = nowdate()
+
+		riv = frappe.get_doc(
+			doctype="Repost Item Valuation",
+			item_code="_Test Item",
+			warehouse="_Test Warehouse - _TC",
+			based_on="Item and Warehouse",
+			posting_date=today,
+			posting_time="00:01:00",
+		)
+		riv.flags.dont_run_in_test = True # keep it queued
+		riv.submit()
+
+		stock_settings = frappe.get_doc("Stock Settings")
+		stock_settings.stock_frozen_upto = today
+
+		self.assertRaises(PendingRepostingError, stock_settings.save)
+
+		riv.set_status("Skipped")
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index a38dfa5..a00d63e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -103,6 +103,8 @@
 		self.set_actual_qty()
 		self.calculate_rate_and_amount()
 		self.validate_putaway_capacity()
+		self.reset_default_field_value("from_warehouse", "items", "s_warehouse")
+		self.reset_default_field_value("to_warehouse", "items", "t_warehouse")
 
 	def on_submit(self):
 		self.update_stock_ledger()
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 1de48b6..c1293cb 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -11,6 +11,8 @@
 from frappe.utils import cint
 from frappe.utils.html_utils import clean_html
 
+from erpnext.stock.utils import check_pending_reposting
+
 
 class StockSettings(Document):
 	def validate(self):
@@ -36,6 +38,7 @@
 		self.validate_warehouses()
 		self.cant_change_valuation_method()
 		self.validate_clean_description_html()
+		self.validate_pending_reposts()
 
 	def validate_warehouses(self):
 		warehouse_fields = ["default_warehouse", "sample_retention_warehouse"]
@@ -64,6 +67,11 @@
 			# changed to text
 			frappe.enqueue('erpnext.stock.doctype.stock_settings.stock_settings.clean_all_descriptions', now=frappe.flags.in_test)
 
+	def validate_pending_reposts(self):
+		if self.stock_frozen_upto:
+			check_pending_reposting(self.stock_frozen_upto)
+
+
 	def on_update(self):
 		self.toggle_warehouse_field_for_inter_warehouse_transfer()
 
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 72d8098..3b1ae3b 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -12,6 +12,7 @@
 
 
 class InvalidWarehouseCompany(frappe.ValidationError): pass
+class PendingRepostingError(frappe.ValidationError): pass
 
 def get_stock_value_from_bin(warehouse=None, item_code=None):
 	values = {}
@@ -417,3 +418,28 @@
 		{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
 	if reposting_in_progress:
 		frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1)
+
+def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool:
+	"""Check if there are pending reposting job till the specified posting date."""
+
+	filters = {
+		"docstatus": 1,
+		"status": ["in", ["Queued","In Progress", "Failed"]],
+		"posting_date": ["<=", posting_date],
+	}
+
+	reposting_pending =  frappe.db.exists("Repost Item Valuation", filters)
+	if reposting_pending and throw_error:
+		msg = _("Stock/Accounts can not be frozen as processing of backdated entries is going on. Please try again later.")
+		frappe.msgprint(msg,
+				raise_exception=PendingRepostingError,
+				title="Stock Reposting Ongoing",
+				indicator="red",
+				primary_action={
+					"label": _("Show pending entries"),
+					"client_action": "erpnext.route_to_pending_reposts",
+					"args": filters,
+				}
+			)
+
+	return bool(reposting_pending)
diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json
index 14712f8..3ff7d02 100644
--- a/erpnext/support/doctype/issue/issue.json
+++ b/erpnext/support/doctype/issue/issue.json
@@ -24,12 +24,10 @@
   "service_level_section",
   "service_level_agreement",
   "response_by",
-  "response_by_variance",
   "reset_service_level_agreement",
   "cb",
   "agreement_status",
   "resolution_by",
-  "resolution_by_variance",
   "service_level_agreement_creation",
   "on_hold_since",
   "total_hold_time",
@@ -123,7 +121,6 @@
    "search_index": 1
   },
   {
-   "default": "Medium",
    "fieldname": "priority",
    "fieldtype": "Link",
    "in_list_view": 1,
@@ -319,22 +316,6 @@
    "label": "Via Customer Portal"
   },
   {
-   "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
-   "fieldname": "response_by_variance",
-   "fieldtype": "Duration",
-   "hide_seconds": 1,
-   "label": "Response By Variance",
-   "read_only": 1
-  },
-  {
-   "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
-   "fieldname": "resolution_by_variance",
-   "fieldtype": "Duration",
-   "hide_seconds": 1,
-   "label": "Resolution By Variance",
-   "read_only": 1
-  },
-  {
    "fieldname": "service_level_agreement_creation",
    "fieldtype": "Datetime",
    "hidden": 1,
@@ -391,12 +372,12 @@
    "read_only": 1
   },
   {
-   "default": "Ongoing",
+   "default": "First Response Due",
    "depends_on": "eval: doc.service_level_agreement",
    "fieldname": "agreement_status",
    "fieldtype": "Select",
    "label": "Service Level Agreement Status",
-   "options": "Ongoing\nFulfilled\nFailed",
+   "options": "First Response Due\nResolution Due\nFulfilled\nFailed",
    "read_only": 1
   },
   {
@@ -410,10 +391,11 @@
  "icon": "fa fa-ticket",
  "idx": 7,
  "links": [],
- "modified": "2021-06-10 03:22:27.098898",
+ "modified": "2021-11-24 13:13:10.276630",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Issue",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 0dc3639..d5e5b78 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -87,11 +87,9 @@
 		if replicated_issue.service_level_agreement:
 			replicated_issue.service_level_agreement_creation = now_datetime()
 			replicated_issue.service_level_agreement = None
-			replicated_issue.agreement_status = "Ongoing"
+			replicated_issue.agreement_status = "First Response Due"
 			replicated_issue.response_by = None
-			replicated_issue.response_by_variance = None
 			replicated_issue.resolution_by = None
-			replicated_issue.resolution_by_variance = None
 			replicated_issue.reset_issue_metrics()
 
 		frappe.get_doc(replicated_issue).insert()
diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js
index e04498e..5bfecb0 100644
--- a/erpnext/support/doctype/issue/issue_list.js
+++ b/erpnext/support/doctype/issue/issue_list.js
@@ -18,7 +18,6 @@
 	},
 	get_indicator: function(doc) {
 		if (doc.status === 'Open') {
-			if (!doc.priority) doc.priority = 'Medium';
 			const color = {
 				'Low': 'yellow',
 				'Medium': 'orange',
diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py
index ab9a444b..14cec46 100644
--- a/erpnext/support/doctype/issue/test_issue.py
+++ b/erpnext/support/doctype/issue/test_issue.py
@@ -1,10 +1,10 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 
-import datetime
 import unittest
 
 import frappe
+from frappe import _
 from frappe.core.doctype.user_permission.test_user_permission import create_user
 from frappe.utils import flt, get_datetime
 
@@ -83,30 +83,6 @@
 
 		self.assertEqual(issue.agreement_status, 'Fulfilled')
 
-	def test_issue_metrics(self):
-		creation = get_datetime("2020-03-04 4:00")
-
-		issue = make_issue(creation, index=1)
-		create_communication(issue.name, "test@example.com", "Received", creation)
-
-		creation = get_datetime("2020-03-04 4:15")
-		create_communication(issue.name, "test@admin.com", "Sent", creation)
-
-		creation = get_datetime("2020-03-04 5:00")
-		create_communication(issue.name, "test@example.com", "Received", creation)
-
-		creation = get_datetime("2020-03-04 5:05")
-		create_communication(issue.name, "test@admin.com", "Sent", creation)
-
-		frappe.flags.current_time = get_datetime("2020-03-04 5:05")
-		issue.reload()
-		issue.status = 'Closed'
-		issue.save()
-
-		self.assertEqual(issue.avg_response_time, 600)
-		self.assertEqual(issue.resolution_time, 3900)
-		self.assertEqual(issue.user_resolution_time, 1200)
-
 	def test_hold_time_on_replied(self):
 		creation = get_datetime("2020-03-04 4:00")
 
@@ -142,6 +118,142 @@
 		issue.reload()
 		self.assertEqual(flt(issue.total_hold_time, 2), 2700)
 
+	def test_issue_close_after_on_hold(self):
+		frappe.flags.current_time = get_datetime("2021-11-01 19:00")
+
+		issue = make_issue(frappe.flags.current_time, index=1)
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+
+		# send a reply within SLA
+		frappe.flags.current_time = get_datetime("2021-11-02 11:00")
+		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
+
+		issue.reload()
+		issue.status = 'Replied'
+		issue.save()
+
+		self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
+
+		# close the issue after being on hold for 20 days
+		frappe.flags.current_time = get_datetime("2021-11-22 01:00")
+		issue.status = 'Closed'
+		issue.save()
+
+		self.assertEqual(issue.resolution_by, get_datetime('2021-11-22 06:00:00'))
+		self.assertEqual(issue.resolution_date, get_datetime('2021-11-22 01:00:00'))
+		self.assertEqual(issue.agreement_status, 'Fulfilled')
+
+	def test_issue_open_after_closed(self):
+
+		# Created on -> 1 pm, Response Time -> 4 hrs, Resolution Time -> 6 hrs
+		frappe.flags.current_time = get_datetime("2021-11-01 13:00")
+		issue = make_issue(frappe.flags.current_time, index=1, issue_type='Critical') # Applies 24hr working time SLA
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+		self.assertEquals(issue.agreement_status, 'First Response Due')
+		self.assertEquals(issue.response_by, get_datetime("2021-11-01 17:00"))
+		self.assertEquals(issue.resolution_by, get_datetime("2021-11-01 19:00"))
+
+		# Replied on → 2 pm
+		frappe.flags.current_time = get_datetime("2021-11-01 14:00")
+		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
+		issue.reload()
+		issue.status = 'Replied'
+		issue.save()
+		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertEquals(issue.on_hold_since, frappe.flags.current_time)
+		self.assertEquals(issue.first_responded_on, frappe.flags.current_time)
+
+		# Customer Replied → 3 pm
+		frappe.flags.current_time = get_datetime("2021-11-01 15:00")
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+		issue.reload()
+		self.assertEquals(issue.status, 'Open')
+		# Hold Time + 1 Hrs
+		self.assertEquals(issue.total_hold_time, 3600)
+		# Resolution By should increase by one hrs
+		self.assertEquals(issue.resolution_by, get_datetime("2021-11-01 20:00"))
+
+		# Replied on → 4 pm, Open → 1 hr, Resolution Due → 8 pm
+		frappe.flags.current_time = get_datetime("2021-11-01 16:00")
+		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
+		issue.reload()
+		issue.status = 'Replied'
+		issue.save()
+		self.assertEquals(issue.agreement_status, 'Resolution Due')
+
+		# Customer Closed → 10 pm
+		frappe.flags.current_time = get_datetime("2021-11-01 22:00")
+		issue.status = 'Closed'
+		issue.save()
+		# Hold Time + 6 Hrs
+		self.assertEquals(issue.total_hold_time, 3600 + 21600)
+		# Resolution By should increase by 6 hrs
+		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 02:00"))
+		self.assertEquals(issue.agreement_status, 'Fulfilled')
+		self.assertEquals(issue.resolution_date, frappe.flags.current_time)
+
+		# Customer Open → 3 am i.e after resolution by is crossed
+		frappe.flags.current_time = get_datetime("2021-11-02 03:00")
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+		issue.reload()
+		# Since issue was Resolved, Resolution By should be increased by 5 hrs (3am - 10pm)
+		self.assertEquals(issue.total_hold_time, 3600 + 21600 + 18000)
+		# Resolution By should increase by 5 hrs
+		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 07:00"))
+		self.assertEquals(issue.agreement_status, 'Resolution Due')
+		self.assertFalse(issue.resolution_date)
+
+		# We Closed → 4 am, SLA should be Fulfilled
+		frappe.flags.current_time = get_datetime("2021-11-02 04:00")
+		issue.status = 'Closed'
+		issue.save()
+		self.assertEquals(issue.resolution_by, get_datetime("2021-11-02 07:00"))
+		self.assertEquals(issue.agreement_status, 'Fulfilled')
+		self.assertEquals(issue.resolution_date, frappe.flags.current_time)
+
+	def test_recording_of_assignment_on_first_reponse_failure(self):
+		from frappe.desk.form.assign_to import add as add_assignment
+
+		frappe.flags.current_time = get_datetime("2021-11-01 19:00")
+
+		issue = make_issue(frappe.flags.current_time, index=1)
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+		add_assignment({
+			'doctype': issue.doctype,
+			'name': issue.name,
+			'assign_to': ['test@admin.com']
+		})
+		issue.reload()
+
+		# send a reply failing response SLA
+		frappe.flags.current_time = get_datetime("2021-11-02 15:00")
+		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
+
+		# assert if a new timeline item has been added
+		# to record the assignment
+		comment = frappe.db.exists('Comment', {
+			'reference_doctype': 'Issue',
+			'reference_name': issue.name,
+			'comment_type': 'Assigned',
+			'content': _('First Response SLA Failed by {}').format('test')
+		})
+		self.assertTrue(comment)
+
+	def test_agreement_status_on_response(self):
+		frappe.flags.current_time = get_datetime("2021-11-01 19:00")
+
+		issue = make_issue(frappe.flags.current_time, index=1)
+		create_communication(issue.name, "test@example.com", "Received", frappe.flags.current_time)
+		self.assertTrue(issue.status == 'Open')
+
+		# send a reply within response SLA
+		frappe.flags.current_time = get_datetime("2021-11-02 11:00")
+		create_communication(issue.name, "test@admin.com", "Sent", frappe.flags.current_time)
+
+		issue.reload()
+		self.assertEquals(issue.first_responded_on, frappe.flags.current_time)
+		self.assertEquals(issue.agreement_status, 'Resolution Due')
+
 class TestFirstResponseTime(TestSetUp):
 	# working hours used in all cases: Mon-Fri, 10am to 6pm
 	# all dates are in the mm-dd-yyyy format
@@ -355,12 +467,18 @@
 def create_issue_and_communication(issue_creation, first_responded_on):
 	issue = make_issue(issue_creation, index=1)
 	sender = create_user("test@admin.com")
+	frappe.flags.current_time = first_responded_on
 	create_communication(issue.name, sender.email, "Sent", first_responded_on)
 	issue.reload()
 
 	return issue
 
 def make_issue(creation=None, customer=None, index=0, priority=None, issue_type=None):
+	if issue_type and not frappe.db.exists('Issue Type', issue_type):
+		doc = frappe.new_doc('Issue Type')
+		doc.name = issue_type
+		doc.insert()
+
 	issue = frappe.get_doc({
 		"doctype": "Issue",
 		"subject": "Service Level Agreement Issue {0}".format(index),
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
index ae2080c..bfbffe2 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js
@@ -22,10 +22,41 @@
 	refresh: function(frm) {
 		frm.trigger('fetch_status_fields');
 		frm.trigger('toggle_resolution_fields');
+		frm.trigger('default_service_level_agreement');
+		frm.trigger('entity');
+	},
+
+	default_service_level_agreement: function(frm) {
+		const field = frm.get_field('default_service_level_agreement');
+		if (frm.doc.default_service_level_agreement) {
+			field.set_description(__('SLA will be applied on every {0}', [frm.doc.document_type]));
+		} else {
+			field.set_description(__('Enable to apply SLA on every {0}', [frm.doc.document_type]));
+		}
 	},
 
 	document_type: function(frm) {
 		frm.trigger('fetch_status_fields');
+		frm.trigger('default_service_level_agreement');
+	},
+
+	entity_type: function(frm) {
+		frm.set_value('entity', undefined);
+	},
+
+	entity: function(frm) {
+		const field = frm.get_field('entity');
+		if (frm.doc.entity) {
+			const and_descendants = frm.doc.entity_type != 'Customer' ? ' ' + __('or its descendants') : '';
+			field.set_description(
+				__('SLA will be applied if {1} is set as {2}{3}', [
+					frm.doc.document_type, frm.doc.entity_type,
+					frm.doc.entity, and_descendants
+				])
+			);
+		} else {
+			field.set_description('');
+		}
 	},
 
 	fetch_status_fields: function(frm) {
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index 5f470aa..1698e23 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -6,22 +6,17 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "enabled",
-  "section_break_2",
   "document_type",
-  "default_service_level_agreement",
   "default_priority",
   "column_break_2",
   "service_level",
-  "holiday_list",
-  "entity_section",
-  "entity_type",
-  "column_break_10",
-  "entity",
+  "enabled",
   "filters_section",
-  "condition",
+  "default_service_level_agreement",
+  "entity_type",
+  "entity",
   "column_break_15",
-  "condition_description",
+  "condition",
   "agreement_details_section",
   "start_date",
   "column_break_7",
@@ -31,8 +26,10 @@
   "priorities",
   "status_details",
   "sla_fulfilled_on",
+  "column_break_22",
   "pause_sla_on",
   "support_and_resolution_section_break",
+  "holiday_list",
   "support_and_resolution"
  ],
  "fields": [
@@ -42,7 +39,8 @@
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Service Level Name",
-   "reqd": 1
+   "reqd": 1,
+   "set_only_once": 1
   },
   {
    "fieldname": "holiday_list",
@@ -56,10 +54,10 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "eval: !doc.default_service_level_agreement",
+   "depends_on": "eval: doc.document_type",
    "fieldname": "agreement_details_section",
    "fieldtype": "Section Break",
-   "label": "Agreement Details"
+   "label": "Valid From"
   },
   {
    "fieldname": "start_date",
@@ -72,7 +70,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "eval: !doc.default_service_level_agreement",
    "fieldname": "end_date",
    "fieldtype": "Date",
    "label": "End Date"
@@ -80,7 +77,7 @@
   {
    "fieldname": "response_and_resolution_time_section",
    "fieldtype": "Section Break",
-   "label": "Response and Resolution Time"
+   "label": "Response and Resolution"
   },
   {
    "fieldname": "support_and_resolution_section_break",
@@ -90,6 +87,7 @@
   {
    "fieldname": "support_and_resolution",
    "fieldtype": "Table",
+   "label": "Working Hours",
    "options": "Service Day",
    "reqd": 1
   },
@@ -101,10 +99,7 @@
    "reqd": 1
   },
   {
-   "fieldname": "column_break_10",
-   "fieldtype": "Column Break"
-  },
-  {
+   "depends_on": "eval: !doc.default_service_level_agreement",
    "fieldname": "entity",
    "fieldtype": "Dynamic Link",
    "in_list_view": 1,
@@ -114,11 +109,6 @@
   },
   {
    "depends_on": "eval: !doc.default_service_level_agreement",
-   "fieldname": "entity_section",
-   "fieldtype": "Section Break",
-   "label": "Entity"
-  },
-  {
    "fieldname": "entity_type",
    "fieldtype": "Select",
    "in_standard_filter": 1,
@@ -126,11 +116,6 @@
    "options": "\nCustomer\nCustomer Group\nTerritory"
   },
   {
-   "fieldname": "section_break_2",
-   "fieldtype": "Section Break",
-   "hide_border": 1
-  },
-  {
    "default": "0",
    "fieldname": "default_service_level_agreement",
    "fieldtype": "Check",
@@ -152,7 +137,7 @@
   {
    "fieldname": "document_type",
    "fieldtype": "Link",
-   "label": "Document Type",
+   "label": "Apply On",
    "options": "DocType",
    "reqd": 1,
    "set_only_once": 1
@@ -164,6 +149,7 @@
    "label": "Enabled"
   },
   {
+   "depends_on": "document_type",
    "fieldname": "status_details",
    "fieldtype": "Section Break",
    "label": "Status Details"
@@ -182,28 +168,31 @@
    "label": "Apply SLA for Resolution Time"
   },
   {
+   "depends_on": "document_type",
    "fieldname": "filters_section",
    "fieldtype": "Section Break",
-   "label": "Assignment Condition"
+   "label": "Assignment Conditions"
   },
   {
    "fieldname": "column_break_15",
    "fieldtype": "Column Break"
   },
   {
+   "depends_on": "eval: !doc.default_service_level_agreement",
+   "description": "Simple Python Expression, Example: doc.status == 'Open' and doc.issue_type == 'Bug'",
    "fieldname": "condition",
    "fieldtype": "Code",
    "label": "Condition",
-   "options": "Python"
+   "max_height": "7rem",
+   "options": "PythonExpression"
   },
   {
-   "fieldname": "condition_description",
-   "fieldtype": "HTML",
-   "options": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Open\"<br>doc.due_date==nowdate()<br>doc.total &gt; 40000\n</pre>"
+   "fieldname": "column_break_22",
+   "fieldtype": "Column Break"
   }
  ],
  "links": [],
- "modified": "2021-10-02 11:32:55.556024",
+ "modified": "2021-11-26 15:45:33.289911",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Service Level Agreement",
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 5f8f83d..50f31fd 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -10,7 +10,6 @@
 from frappe.model.document import Document
 from frappe.utils import (
 	add_to_date,
-	cint,
 	get_datetime,
 	get_datetime_str,
 	get_link_to_form,
@@ -22,6 +21,7 @@
 	time_diff_in_seconds,
 	to_timedelta,
 )
+from frappe.utils.nestedset import get_ancestors_of
 from frappe.utils.safe_exec import get_safe_globals
 
 from erpnext.support.doctype.issue.issue import get_holidays
@@ -248,7 +248,7 @@
 
 	customer = doc.get('customer')
 	or_filters.append(
-		["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]]
+		["Service Level Agreement", "entity", "in", [customer] + get_customer_group(customer) + get_customer_territory(customer)]
 	)
 
 	default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]]
@@ -275,11 +275,23 @@
 	return {"doc": doc.as_dict(), "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))}
 
 def get_customer_group(customer):
-	return frappe.db.get_value("Customer", customer, "customer_group") if customer else None
+	customer_groups = []
+	customer_group = frappe.db.get_value("Customer", customer, "customer_group") if customer else None
+	if customer_group:
+		ancestors = get_ancestors_of("Customer Group", customer_group)
+		customer_groups = [customer_group] + ancestors
+
+	return customer_groups
 
 
 def get_customer_territory(customer):
-	return frappe.db.get_value("Customer", customer, "territory") if customer else None
+	customer_territories = []
+	customer_territory = frappe.db.get_value("Customer", customer, "territory") if customer else None
+	if customer_territory:
+		ancestors = get_ancestors_of("Territory", customer_territory)
+		customer_territories = [customer_territory] + ancestors
+
+	return customer_territories
 
 
 @frappe.whitelist()
@@ -299,7 +311,7 @@
 	if customer:
 		# Include SLA with No Entity and Entity Type
 		or_filters.append(
-			["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer), ""]]
+			["Service Level Agreement", "entity", "in", [""] + [customer] + get_customer_group(customer) + get_customer_territory(customer)]
 		)
 
 	return {
@@ -337,84 +349,135 @@
 
 def apply(doc, method=None):
 	# Applies SLA to document on validate
-	if frappe.flags.in_patch or frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_setup_wizard or \
-		doc.doctype not in get_documents_with_active_service_level_agreement():
+	if (
+		frappe.flags.in_patch
+		or frappe.flags.in_migrate
+		or frappe.flags.in_install
+		or frappe.flags.in_setup_wizard
+		or doc.doctype not in get_documents_with_active_service_level_agreement()
+	):
 		return
 
-	service_level_agreement = get_active_service_level_agreement_for(doc)
+	sla = get_active_service_level_agreement_for(doc)
 
-	if not service_level_agreement:
+	if not sla:
 		return
 
-	set_sla_properties(doc, service_level_agreement)
+	process_sla(doc, sla)
 
 
-def set_sla_properties(doc, service_level_agreement):
-	if frappe.db.exists(doc.doctype, doc.name):
-		from_db = frappe.get_doc(doc.doctype, doc.name)
-	else:
-		from_db = frappe._dict({})
-
-	meta = frappe.get_meta(doc.doctype)
-
-	if meta.has_field("customer") and service_level_agreement.customer and doc.get("customer") and \
-		not service_level_agreement.customer == doc.get("customer"):
-		frappe.throw(_("Service Level Agreement {0} is specific to Customer {1}").format(service_level_agreement.name,
-			service_level_agreement.customer))
-
-	doc.service_level_agreement = service_level_agreement.name
-	doc.priority = doc.get("priority") or service_level_agreement.default_priority
-	priority = get_priority(doc)
+def process_sla(doc, sla):
 
 	if not doc.creation:
 		doc.creation = now_datetime(doc.get("owner"))
-
-		if meta.has_field("service_level_agreement_creation"):
+		if doc.meta.has_field("service_level_agreement_creation"):
 			doc.service_level_agreement_creation = now_datetime(doc.get("owner"))
 
+	doc.service_level_agreement = sla.name
+	doc.priority = doc.get("priority") or sla.default_priority
+
+	handle_status_change(doc, sla.apply_sla_for_resolution)
+	update_response_and_resolution_metrics(doc, sla.apply_sla_for_resolution)
+	update_agreement_status(doc, sla.apply_sla_for_resolution)
+
+
+def handle_status_change(doc, apply_sla_for_resolution):
+	now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
+	prev_status = frappe.db.get_value(doc.doctype, doc.name, 'status')
+
+	hold_statuses = get_hold_statuses(doc.service_level_agreement)
+	fulfillment_statuses = get_fulfillment_statuses(doc.service_level_agreement)
+
+	def is_hold_status(status):
+		return status in hold_statuses
+
+	def is_fulfilled_status(status):
+		return status in fulfillment_statuses
+
+	def is_open_status(status):
+		return status not in hold_statuses and status not in fulfillment_statuses
+
+	def set_first_response():
+		if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+			doc.first_responded_on = now_time
+			if get_datetime(doc.get('first_responded_on')) > get_datetime(doc.get('response_by')):
+				record_assigned_users_on_failure(doc)
+
+	def calculate_hold_hours():
+		# In case issue was closed and after few days it has been opened
+		# The hold time should be calculated from resolution_date
+
+		on_hold_since = doc.resolution_date or doc.on_hold_since
+		if on_hold_since:
+			current_hold_hours = time_diff_in_seconds(now_time, on_hold_since)
+			doc.total_hold_time = (doc.total_hold_time or 0) + current_hold_hours
+		doc.on_hold_since = None
+
+	if ((is_open_status(prev_status) and not is_open_status(doc.status)) or doc.flags.on_first_reply):
+		set_first_response()
+
+	# Open to Replied
+	if is_open_status(prev_status) and is_hold_status(doc.status):
+		# Issue is on hold -> Set on_hold_since
+		doc.on_hold_since = now_time
+
+	# Replied to Open
+	if is_hold_status(prev_status) and is_open_status(doc.status):
+		# Issue was on hold -> Calculate Total Hold Time
+		calculate_hold_hours()
+		# Issue is open -> reset resolution_date
+		reset_expected_response_and_resolution(doc)
+		reset_resolution_metrics(doc)
+
+	# Open to Closed
+	if is_open_status(prev_status) and is_fulfilled_status(doc.status):
+		# Issue is closed -> Set resolution_date
+		doc.resolution_date = now_time
+		set_resolution_time(doc)
+
+	# Closed to Open
+	if is_fulfilled_status(prev_status) and is_open_status(doc.status):
+		# Issue was closed -> Calculate Total Hold Time from resolution_date
+		calculate_hold_hours()
+		# Issue is open -> reset resolution_date
+		reset_expected_response_and_resolution(doc)
+		reset_resolution_metrics(doc)
+
+	# Closed to Replied
+	if is_fulfilled_status(prev_status) and is_hold_status(doc.status):
+		# Issue was closed -> Calculate Total Hold Time from resolution_date
+		calculate_hold_hours()
+		# Issue is on hold -> Set on_hold_since
+		doc.on_hold_since = now_time
+
+	# Replied to Closed
+	if is_hold_status(prev_status) and is_fulfilled_status(doc.status):
+		# Issue was on hold -> Calculate Total Hold Time
+		calculate_hold_hours()
+		# Issue is closed -> Set resolution_date
+		if apply_sla_for_resolution:
+			doc.resolution_date = now_time
+			set_resolution_time(doc)
+
+
+def get_fulfillment_statuses(service_level_agreement):
+	return [entry.status for entry in frappe.db.get_all("SLA Fulfilled On Status", filters={
+		"parent": service_level_agreement
+	}, fields=["status"])]
+
+
+def get_hold_statuses(service_level_agreement):
+	return [entry.status for entry in frappe.db.get_all("Pause SLA On Status", filters={
+		"parent": service_level_agreement
+	}, fields=["status"])]
+
+
+def update_response_and_resolution_metrics(doc, apply_sla_for_resolution):
+	priority = get_response_and_resolution_duration(doc)
 	start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation)
-
-	set_response_by_and_variance(doc, meta, start_date_time, priority)
-	if service_level_agreement.apply_sla_for_resolution:
-		set_resolution_by_and_variance(doc, meta, start_date_time, priority)
-
-	update_status(doc, from_db, meta)
-
-
-def update_status(doc, from_db, meta):
-	if meta.has_field("status"):
-		if meta.has_field("first_responded_on") and doc.status != "Open" and \
-			from_db.status == "Open" and not doc.first_responded_on:
-			doc.first_responded_on = frappe.flags.current_time or now_datetime(doc.get("owner"))
-
-		if meta.has_field("service_level_agreement") and doc.service_level_agreement:
-			# mark sla status as fulfilled based on the configuration
-			fulfillment_statuses = [entry.status for entry in frappe.db.get_all("SLA Fulfilled On Status", filters={
-				"parent": doc.service_level_agreement
-			}, fields=["status"])]
-
-			if doc.status in fulfillment_statuses and from_db.status not in fulfillment_statuses:
-				apply_sla_for_resolution = frappe.db.get_value("Service Level Agreement", doc.service_level_agreement,
-					"apply_sla_for_resolution")
-
-				if apply_sla_for_resolution and meta.has_field("resolution_date"):
-					doc.resolution_date = frappe.flags.current_time or now_datetime(doc.get("owner"))
-
-				if meta.has_field("agreement_status") and from_db.agreement_status == "Ongoing":
-					set_service_level_agreement_variance(doc.doctype, doc.name)
-					update_agreement_status(doc, meta)
-
-				if apply_sla_for_resolution:
-					set_resolution_time(doc, meta)
-					set_user_resolution_time(doc, meta)
-
-		if doc.status == "Open" and from_db.status != "Open":
-			# if no date, it should be set as None and not a blank string "", as per mysql strict config
-			# enable SLA and variance on Reopen
-			reset_metrics(doc, meta)
-			set_service_level_agreement_variance(doc.doctype, doc.name)
-
-	handle_hold_time(doc, meta, from_db.status)
+	set_response_by(doc, start_date_time, priority)
+	if apply_sla_for_resolution:
+		set_resolution_by(doc, start_date_time, priority)
 
 
 def get_expected_time_for(parameter, service_level, start_date_time):
@@ -485,37 +548,13 @@
 	return support_days
 
 
-def set_service_level_agreement_variance(doctype, doc=None):
+def set_resolution_time(doc):
+	start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation)
+	if doc.meta.has_field("resolution_time"):
+		doc.resolution_time = time_diff_in_seconds(doc.resolution_date, start_date_time)
 
-	filters = {"status": "Open", "agreement_status": "Ongoing"}
-
-	if doc:
-		filters = {"name": doc}
-
-	for entry in frappe.get_all(doctype, filters=filters):
-		current_doc = frappe.get_doc(doctype, entry.name)
-		current_time = frappe.flags.current_time or now_datetime(current_doc.get("owner"))
-		apply_sla_for_resolution = frappe.db.get_value("Service Level Agreement", current_doc.service_level_agreement,
-			"apply_sla_for_resolution")
-
-		if not current_doc.first_responded_on: # first_responded_on set when first reply is sent to customer
-			variance = round(time_diff_in_seconds(current_doc.response_by, current_time), 2)
-			frappe.db.set_value(current_doc.doctype, current_doc.name, "response_by_variance", variance, update_modified=False)
-
-			if variance < 0:
-				frappe.db.set_value(current_doc.doctype, current_doc.name, "agreement_status", "Failed", update_modified=False)
-
-		if apply_sla_for_resolution and not current_doc.get("resolution_date"): # resolution_date set when issue has been closed
-			variance = round(time_diff_in_seconds(current_doc.resolution_by, current_time), 2)
-			frappe.db.set_value(current_doc.doctype, current_doc.name, "resolution_by_variance", variance, update_modified=False)
-
-			if variance < 0:
-				frappe.db.set_value(current_doc.doctype, current_doc.name, "agreement_status", "Failed", update_modified=False)
-
-
-def set_user_resolution_time(doc, meta):
 	# total time taken by a user to close the issue apart from wait_time
-	if not meta.has_field("user_resolution_time"):
+	if not doc.meta.has_field("user_resolution_time"):
 		return
 
 	communications = frappe.get_all("Communication", filters={
@@ -531,7 +570,7 @@
 				pending_time.append(wait_time)
 
 	total_pending_time = sum(pending_time)
-	resolution_time_in_secs = time_diff_in_seconds(doc.resolution_date, doc.creation)
+	resolution_time_in_secs = time_diff_in_seconds(doc.resolution_date, start_date_time)
 	doc.user_resolution_time = resolution_time_in_secs - total_pending_time
 
 
@@ -548,12 +587,12 @@
 			frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement))
 
 
-def get_priority(doc):
-	service_level_agreement = frappe.get_doc("Service Level Agreement", doc.service_level_agreement)
-	priority = service_level_agreement.get_service_level_agreement_priority(doc.priority)
+def get_response_and_resolution_duration(doc):
+	sla = frappe.get_doc("Service Level Agreement", doc.service_level_agreement)
+	priority = sla.get_service_level_agreement_priority(doc.priority)
 	priority.update({
-		"support_and_resolution": service_level_agreement.support_and_resolution,
-		"holiday_list": service_level_agreement.holiday_list
+		"support_and_resolution": sla.support_and_resolution,
+		"holiday_list": sla.holiday_list
 	})
 	return priority
 
@@ -572,122 +611,99 @@
 	}).insert(ignore_permissions=True)
 
 	doc.service_level_agreement_creation = now_datetime(doc.get("owner"))
-	doc.set_response_and_resolution_time(priority=doc.priority, service_level_agreement=doc.service_level_agreement)
-	doc.agreement_status = "Ongoing"
 	doc.save()
 
 
-def reset_metrics(doc, meta):
-	if meta.has_field("resolution_date"):
+def reset_resolution_metrics(doc):
+	if doc.meta.has_field("resolution_date"):
 		doc.resolution_date = None
 
-	if not meta.has_field("resolution_time"):
+	if doc.meta.has_field("resolution_time"):
 		doc.resolution_time = None
 
-	if not meta.has_field("user_resolution_time"):
+	if doc.meta.has_field("user_resolution_time"):
 		doc.user_resolution_time = None
 
-	if meta.has_field("agreement_status"):
-		doc.agreement_status = "Ongoing"
-
-
-def set_resolution_time(doc, meta):
-	# total time taken from issue creation to closing
-	if not meta.has_field("resolution_time"):
-		return
-
-	doc.resolution_time = time_diff_in_seconds(doc.resolution_date, doc.creation)
+	if doc.meta.has_field("agreement_status"):
+		doc.agreement_status = "First Response Due"
 
 
 # called via hooks on communication update
-def update_hold_time(doc, status):
+def on_communication_update(doc, status):
+	if doc.communication_type == "Comment":
+		return
+
 	parent = get_parent_doc(doc)
 	if not parent:
 		return
 
-	if doc.communication_type == "Comment":
+	if not parent.meta.has_field('service_level_agreement'):
 		return
 
-	status_field = parent.meta.get_field("status")
-	if status_field:
-		options = (status_field.options or "").splitlines()
+	for_resolution = frappe.db.get_value('Service Level Agreement', parent.service_level_agreement, 'apply_sla_for_resolution')
 
-		# if status has a "Replied" option, then handle hold time
-		if ("Replied" in options) and doc.sent_or_received == "Received":
-			meta = frappe.get_meta(parent.doctype)
-			handle_hold_time(parent, meta, 'Replied')
+	if (
+		doc.sent_or_received == "Received" # a reply is received
+		and parent.get('status') == 'Open' # issue status is set as open from communication.py
+		and parent._doc_before_save
+		and parent.get('status') != parent._doc_before_save.get('status') # status changed
+	):
+		# undo the status change in db
+		# since prev status is fetched from db
+		frappe.db.set_value(parent.doctype, parent.name, 'status', parent._doc_before_save.get('status'))
+
+	elif (
+		doc.sent_or_received == "Sent" # a reply is sent
+		and parent.get('first_responded_on') # first_responded_on is set from communication.py
+		and parent._doc_before_save
+		and not parent._doc_before_save.get('first_responded_on') # first_responded_on was not set
+	):
+		# reset first_responded_on since it will be handled/set later on
+		parent.first_responded_on = None
+		parent.flags.on_first_reply = True
+
+	handle_status_change(parent, for_resolution)
+	update_response_and_resolution_metrics(parent, for_resolution)
+	update_agreement_status(parent, for_resolution)
+
+	parent.save()
 
 
-def handle_hold_time(doc, meta, status):
-	if meta.has_field("service_level_agreement") and doc.service_level_agreement:
-		# set response and resolution variance as None as the issue is on Hold for status as Replied
-		hold_statuses = [entry.status for entry in frappe.db.get_all("Pause SLA On Status", filters={
-				"parent": doc.service_level_agreement
-			}, fields=["status"])]
-
-		if not hold_statuses:
-			return
-
-		if meta.has_field("status") and doc.status in hold_statuses and status not in hold_statuses:
-			apply_hold_status(doc, meta)
-
-		# calculate hold time when status is changed from any hold status to any non-hold status
-		if meta.has_field("status") and doc.status not in hold_statuses and status in hold_statuses:
-			reset_hold_status_and_update_hold_time(doc, meta)
-
-
-def apply_hold_status(doc, meta):
-	update_values = {'on_hold_since': frappe.flags.current_time or now_datetime(doc.get("owner"))}
-
-	if meta.has_field("first_responded_on") and not doc.first_responded_on:
-		update_values['response_by'] = None
-		update_values['response_by_variance'] = 0
-
-	update_values['resolution_by'] = None
-	update_values['resolution_by_variance'] = 0
-
-	doc.db_set(update_values)
-
-
-def reset_hold_status_and_update_hold_time(doc, meta):
-	hold_time = doc.total_hold_time if meta.has_field("total_hold_time") and doc.total_hold_time else 0
-	now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-	last_hold_time = 0
+def reset_expected_response_and_resolution(doc):
 	update_values = {}
-
-	if meta.has_field("on_hold_since") and doc.on_hold_since:
-		# last_hold_time will be added to the sla variables
-		last_hold_time = time_diff_in_seconds(now_time, doc.on_hold_since)
-		update_values['total_hold_time'] = hold_time + last_hold_time
-
-	# re-calculate SLA variables after issue changes from any hold status to any non-hold status
-	start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation)
-	priority = get_priority(doc)
-	now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-
-	# add hold time to response by variance
-	if meta.has_field("first_responded_on") and not doc.first_responded_on:
-		response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
-		response_by = add_to_date(response_by, seconds=round(last_hold_time))
-		response_by_variance = round(time_diff_in_seconds(response_by, now_time))
-
-		update_values['response_by'] = response_by
-		update_values['response_by_variance'] = response_by_variance + last_hold_time
-
-	# add hold time to resolution by variance
-	if frappe.db.get_value("Service Level Agreement", doc.service_level_agreement, "apply_sla_for_resolution"):
-		resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
-		resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time))
-		resolution_by_variance = round(time_diff_in_seconds(resolution_by, now_time))
-
-		update_values['resolution_by'] = resolution_by
-		update_values['resolution_by_variance'] = resolution_by_variance + last_hold_time
-
-	update_values['on_hold_since'] = None
-
+	if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+		update_values['response_by'] = None
+	if doc.meta.has_field("resolution_by") and not doc.get('resolution_date'):
+		update_values['resolution_by'] = None
 	doc.db_set(update_values)
 
 
+def set_response_by(doc, start_date_time, priority):
+	if doc.meta.has_field("response_by"):
+		doc.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
+		if doc.meta.has_field("total_hold_time") and doc.get('total_hold_time') and not doc.get('first_responded_on'):
+			doc.response_by = add_to_date(doc.response_by, seconds=round(doc.get('total_hold_time')))
+
+
+def set_resolution_by(doc, start_date_time, priority):
+	if doc.meta.has_field("resolution_by"):
+		doc.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
+		if doc.meta.has_field("total_hold_time") and doc.get('total_hold_time'):
+			doc.resolution_by = add_to_date(doc.resolution_by, seconds=round(doc.get('total_hold_time')))
+
+
+def record_assigned_users_on_failure(doc):
+	assigned_users = doc.get_assigned_users()
+	if assigned_users:
+		from frappe.utils import get_fullname
+		assigned_users = ', '.join((get_fullname(user) for user in assigned_users))
+		message = _('First Response SLA Failed by {}').format(assigned_users)
+		doc.add_comment(
+			comment_type='Assigned',
+			text=message
+		)
+
+
 def get_service_level_agreement_fields():
 	return [
 		{
@@ -715,16 +731,10 @@
 			"read_only": 1
 		},
 		{
-			"fieldname": "response_by_variance",
-			"fieldtype": "Duration",
-			"hide_seconds": 1,
-			"label": "Response By Variance",
-			"read_only": 1
-		},
-		{
 			"fieldname": "first_responded_on",
 			"fieldtype": "Datetime",
 			"label": "First Responded On",
+			"no_copy": 1,
 			"read_only": 1
 		},
 		{
@@ -746,11 +756,11 @@
 			"read_only": 1
 		},
 		{
-			"default": "Ongoing",
+			"default": "First Response Due",
 			"fieldname": "agreement_status",
 			"fieldtype": "Select",
 			"label": "Service Level Agreement Status",
-			"options": "Ongoing\nFulfilled\nFailed",
+			"options": "First Response Due\nResolution Due\nFulfilled\nFailed",
 			"read_only": 1
 		},
 		{
@@ -760,13 +770,6 @@
 			"read_only": 1
 		},
 		{
-			"fieldname": "resolution_by_variance",
-			"fieldtype": "Duration",
-			"hide_seconds": 1,
-			"label": "Resolution By Variance",
-			"read_only": 1
-		},
-		{
 			"fieldname": "service_level_agreement_creation",
 			"fieldtype": "Datetime",
 			"hidden": 1,
@@ -786,43 +789,28 @@
 
 def update_agreement_status_on_custom_status(doc):
 	# Update Agreement Fulfilled status using Custom Scripts for Custom Status
-
-	meta = frappe.get_meta(doc.doctype)
-	now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-	if meta.has_field("first_responded_on") and not doc.first_responded_on:
-		# first_responded_on set when first reply is sent to customer
-		doc.response_by_variance = round(time_diff_in_seconds(doc.response_by, now_time), 2)
-
-	if meta.has_field("resolution_date") and not doc.resolution_date:
-		# resolution_date set when issue has been closed
-		doc.resolution_by_variance = round(time_diff_in_seconds(doc.resolution_by, now_time), 2)
-
-	if meta.has_field("agreement_status"):
-		doc.agreement_status = "Fulfilled" if doc.response_by_variance > 0 and doc.resolution_by_variance > 0 else "Failed"
+	update_agreement_status(doc)
 
 
-def update_agreement_status(doc, meta):
-	if meta.has_field("service_level_agreement") and meta.has_field("agreement_status") and \
-		doc.service_level_agreement and doc.agreement_status == "Ongoing":
-
-		apply_sla_for_resolution = frappe.db.get_value("Service Level Agreement", doc.service_level_agreement,
-			"apply_sla_for_resolution")
-
+def update_agreement_status(doc, apply_sla_for_resolution):
+	if (doc.meta.has_field("agreement_status")):
 		# if SLA is applied for resolution check for response and resolution, else only response
 		if apply_sla_for_resolution:
-			if meta.has_field("response_by_variance") and meta.has_field("resolution_by_variance"):
-				if cint(frappe.db.get_value(doc.doctype, doc.name, "response_by_variance")) < 0 or \
-					cint(frappe.db.get_value(doc.doctype, doc.name, "resolution_by_variance")) < 0:
-
-					doc.agreement_status = "Failed"
-				else:
-					doc.agreement_status = "Fulfilled"
-		else:
-			if meta.has_field("response_by_variance") and \
-				cint(frappe.db.get_value(doc.doctype, doc.name, "response_by_variance")) < 0:
-				doc.agreement_status = "Failed"
-			else:
+			if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+				doc.agreement_status = "First Response Due"
+			elif doc.meta.has_field("resolution_date") and not doc.get('resolution_date'):
+				doc.agreement_status = "Resolution Due"
+			elif get_datetime(doc.get('resolution_date')) <= get_datetime(doc.get('resolution_by')):
 				doc.agreement_status = "Fulfilled"
+			else:
+				doc.agreement_status = "Failed"
+		else:
+			if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
+				doc.agreement_status = "First Response Due"
+			elif get_datetime(doc.get('first_responded_on')) <= get_datetime(doc.get('response_by')):
+				doc.agreement_status = "Fulfilled"
+			else:
+				doc.agreement_status = "Failed"
 
 
 def is_holiday(date, holidays):
@@ -835,23 +823,6 @@
 	return datetime.timedelta(hours=time.hour, minutes=time.minute, seconds=time.second)
 
 
-def set_response_by_and_variance(doc, meta, start_date_time, priority):
-	if meta.has_field("response_by"):
-		doc.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
-
-	if meta.has_field("response_by_variance") and not doc.get('first_responded_on'):
-		now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-		doc.response_by_variance = round(time_diff_in_seconds(doc.response_by, now_time), 2)
-
-def set_resolution_by_and_variance(doc, meta, start_date_time, priority):
-	if meta.has_field("resolution_by"):
-		doc.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
-
-	if meta.has_field("resolution_by_variance") and not doc.get("resolution_date"):
-		now_time = frappe.flags.current_time or now_datetime(doc.get("owner"))
-		doc.resolution_by_variance = round(time_diff_in_seconds(doc.resolution_by, now_time), 2)
-
-
 def now_datetime(user):
 	dt = convert_utc_to_user_timezone(datetime.utcnow(), user)
 	return dt.replace(tzinfo=None)
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index cfbe744..b07c862 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -220,42 +220,6 @@
 		lead.reload()
 		self.assertEqual(lead.agreement_status, 'Fulfilled')
 
-	def test_changing_of_variance_after_response(self):
-		# create lead
-		doctype = "Lead"
-		lead_sla = create_service_level_agreement(
-			default_service_level_agreement=1,
-			holiday_list="__Test Holiday List",
-			entity_type=None, entity=None,
-			response_time=14400,
-			doctype=doctype,
-			sla_fulfilled_on=[{"status": "Replied"}],
-			apply_sla_for_resolution=0
-		)
-		creation = datetime.datetime(2019, 3, 4, 12, 0)
-		lead = make_lead(creation=creation, index=2)
-		self.assertEqual(lead.service_level_agreement, lead_sla.name)
-
-		# set lead as replied to set first responded on
-		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 15, 30)
-		lead.reload()
-		lead.status = 'Replied'
-		lead.save()
-		lead.reload()
-		self.assertEqual(lead.agreement_status, 'Fulfilled')
-
-		# check response_by_variance
-		self.assertEqual(lead.first_responded_on, frappe.flags.current_time)
-		self.assertEqual(lead.response_by_variance, 1800.0)
-
-		# make a change on the document &
-		# check response_by_variance is unchanged
-		frappe.flags.current_time = datetime.datetime(2019, 3, 4, 18, 30)
-		lead.status = 'Open'
-		lead.save()
-		lead.reload()
-		self.assertEqual(lead.response_by_variance, 1800.0)
-
 	def test_service_level_agreement_filters(self):
 		doctype = "Lead"
 		lead_sla = create_service_level_agreement(
@@ -295,7 +259,8 @@
 	return service_level_agreement
 
 def create_service_level_agreement(default_service_level_agreement, holiday_list, response_time, entity_type,
-	entity, resolution_time=0, doctype="Issue", condition="", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1):
+	entity, resolution_time=0, doctype="Issue", condition="", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1,
+	service_level=None, start_time="10:00:00", end_time="18:00:00"):
 
 	make_holiday_list()
 	make_priorities()
@@ -312,7 +277,7 @@
 		"doctype": "Service Level Agreement",
 		"enabled": 1,
 		"document_type": doctype,
-		"service_level": "__Test {} SLA".format(entity_type if entity_type else "Default"),
+		"service_level": service_level or "__Test {} SLA".format(entity_type if entity_type else "Default"),
 		"default_service_level_agreement": default_service_level_agreement,
 		"condition": condition,
 		"default_priority": "Medium",
@@ -345,28 +310,28 @@
 		"support_and_resolution": [
 			{
 				"workday": "Monday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
+				"start_time": start_time,
+				"end_time": end_time,
 			},
 			{
 				"workday": "Tuesday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
+				"start_time": start_time,
+				"end_time": end_time,
 			},
 			{
 				"workday": "Wednesday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
+				"start_time": start_time,
+				"end_time": end_time,
 			},
 			{
 				"workday": "Thursday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
+				"start_time": start_time,
+				"end_time": end_time,
 			},
 			{
 				"workday": "Friday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
+				"start_time": start_time,
+				"end_time": end_time,
 			}
 		]
 	})
@@ -386,7 +351,7 @@
 	if sla:
 		frappe.delete_doc("Service Level Agreement", sla, force=1)
 
-	return frappe.get_doc(service_level_agreement).insert(ignore_permissions=True)
+	return frappe.get_doc(service_level_agreement).insert(ignore_permissions=True, ignore_if_duplicate=True)
 
 
 def create_customer():
@@ -443,6 +408,13 @@
 	create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
 		entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800)
 
+	create_service_level_agreement(
+		default_service_level_agreement=0, holiday_list="__Test Holiday List",
+		entity_type=None, entity=None, response_time=14400, resolution_time=21600,
+		service_level="24-hour-SLA", start_time="00:00:00", end_time="23:59:59",
+		condition="doc.issue_type == 'Critical'"
+	)
+
 def make_holiday_list():
 	holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List")
 	if not holiday_list:
diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py
index 39a5c40..67fe345 100644
--- a/erpnext/support/report/issue_summary/issue_summary.py
+++ b/erpnext/support/report/issue_summary/issue_summary.py
@@ -82,7 +82,8 @@
 		self.sla_status_map = {
 			'SLA Failed': 'failed',
 			'SLA Fulfilled': 'fulfilled',
-			'SLA Ongoing': 'ongoing'
+			'First Response Due': 'first_response_due',
+			'Resolution Due': 'resolution_due'
 		}
 
 		for label, fieldname in self.sla_status_map.items():
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index ca03a78..d46ffb5 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -1847,7 +1847,7 @@
 Overlap in scoring between {0} and {1},Überlappung beim Scoring zwischen {0} und {1},
 Overlapping conditions found between:,Überlagernde Bedingungen gefunden zwischen:,
 Owner,Besitzer,
-PAN,PFANNE,
+PAN,PAN,
 POS,Verkaufsstelle,
 POS Profile,Verkaufsstellen-Profil,
 POS Profile is required to use Point-of-Sale,"POS-Profil ist erforderlich, um Point-of-Sale zu verwenden",
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index 14b3afa..1d8b3a8 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -162,6 +162,28 @@
 
 		return ret
 
+	def reset_default_field_value(self, default_field: str, child_table: str, child_table_field: str):
+		""" Reset "Set default X" fields on forms to avoid confusion.
+
+			example:
+				doc = {
+					"set_from_warehouse": "Warehouse A",
+					"items": [{"from_warehouse": "warehouse B"}, {"from_warehouse": "warehouse A"}],
+				}
+				Since this has dissimilar values in child table, the default field will be erased.
+
+				doc.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
+			"""
+		child_table_values = set()
+
+		for row in self.get(child_table):
+			child_table_values.add(row.get(child_table_field))
+
+		if len(child_table_values) > 1:
+			self.set(default_field, None)
+		else:
+			self.set(default_field, list(child_table_values)[0])
+
 def delete_events(ref_type, ref_name):
 	events = frappe.db.sql_list(""" SELECT
 			distinct `tabEvent`.name