feat: New Payroll module (#21990)

* feat: Moved Document to Payroll Module

* feat: Moved Reports to Payroll Module

* feat: Moved Print fromat With Patch

* feat: Moved Notifiction to Payroll Module and patches

* feat: added dashboard and desk page to Payroll

* feat: Payroll Dashboard

* feat: Module onboarding

* feat: Income tax Deductions Report

* feat: Ecs Checklist Report

* feat: Provident Fund Report

* feat: Professional Fund report and commonified Code

* feat: Total Payments Based On Payment Mode Report

* fix: refactor and added chart Total Payments Based On Payment Mode

* feat: Payroll Settings

* fix: Bank remittance Report

* feat(Payroll based on): Considered unmarked days

* feat: Added Help for condition an formula in Salary structure

* fix: requested changes

* fix: rename report Ecs checklist to salary_payments_via_ecs

* fix: renamed report report/total_payments_based_on_payment_mode

* fix: added role via setup.py for regional report

* feat: added All reports to desk page

* fix: frappe.reload doc in all patches

* fix: codacy

* fix: frappe.reload_doctype for patches

* patch: is_income_tax_component and component_type for salary component

* fix: uncommented code

* test: fixture

* fix: test

* test: test_payment_days_based_on_attendance
diff --git a/erpnext/payroll/__init__.py b/erpnext/payroll/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/__init__.py
diff --git a/erpnext/payroll/dashboard_fixtures.py b/erpnext/payroll/dashboard_fixtures.py
new file mode 100644
index 0000000..ae7a9ff
--- /dev/null
+++ b/erpnext/payroll/dashboard_fixtures.py
@@ -0,0 +1,100 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+import erpnext
+from erpnext.hr.dashboard_fixtures import get_dashboards_chart_doc, get_number_cards_doc
+import json
+from frappe import _
+
+def get_data():
+	return frappe._dict({
+		"dashboards": get_dashboards(),
+		"charts": get_charts(),
+		"number_cards": get_number_cards(),
+	})
+
+def get_dashboards():
+	dashboards = []
+	dashboards.append(get_payroll_dashboard())
+	return dashboards
+
+def get_payroll_dashboard():
+	return {
+		"name": "Payroll",
+		"dashboard_name": "Payroll",
+		"is_default": 1,
+		"charts": [
+			{ "chart": "Outgoing Salary", "width": "Full"},
+			{ "chart": "Designation Wise Salary(Last Month)", "width": "Half"},
+			{ "chart": "Department Wise Salary(Last Month)", "width": "Half"},
+		],
+		"cards": [
+			{"card": "Total Declaration Submitted"},
+			{"card": "Total Salary Structure"},
+			{"card": "Total Incentive Given(Last month)"},
+			{"card": "Total Outgoing Salary(Last month)"},
+		]
+	}
+
+def get_charts():
+	dashboard_charts= [
+		get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line",
+			document_type = "Salary Slip", based_on="end_date",
+			value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1,
+			filters_json = json.dumps([["Salary Slip", "docstatus", "=", 1]]))
+	]
+
+	dashboard_charts.append(
+		get_dashboards_chart_doc('Department Wise Salary(Last Month)', "Group By", "Bar",
+			document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="department",
+			time_interval = "Monthly", aggregate_function_based_on = "rounded_total",
+			filters_json = json.dumps([
+				["Salary Slip", "docstatus", "=", 1],
+				["Salary Slip", "start_date", "Previous","1 month"]
+			])
+		)
+	)
+
+	dashboard_charts.append(
+		get_dashboards_chart_doc('Designation Wise Salary(Last Month)', "Group By", "Bar",
+			document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="designation",
+			time_interval = "Monthly", aggregate_function_based_on = "rounded_total",
+			filters_json = json.dumps([
+				["Salary Slip", "docstatus", "=", 1],
+				["Salary Slip", "start_date", "Previous","1 month"]
+			])
+		)
+	)
+
+	return dashboard_charts
+
+def get_number_cards():
+	number_cards = [get_number_cards_doc("Employee Tax Exemption Declaration", "Total Declaration Submitted", filters_json = json.dumps([
+				["Employee Tax Exemption Declaration", "docstatus", "=","1"],
+				["Employee Tax Exemption Declaration","creation","Previous","1 year"]
+			])
+		)]
+
+	number_cards.append(get_number_cards_doc("Employee Incentive", "Total Incentive Given(Last month)",
+		time_interval = "Monthly", func = "Sum", aggregate_function_based_on = "incentive_amount",
+		filters_json = json.dumps([
+			["Employee Incentive", "docstatus", "=", 1],
+			["Employee Incentive","payroll_date","Previous","1 year"]
+		]))
+	)
+
+	number_cards.append(get_number_cards_doc("Salary Slip", "Total Outgoing Salary(Last month)",
+		time_interval = "Monthly", time_span= "Monthly", func = "Sum", aggregate_function_based_on = "rounded_total",
+		filters_json = json.dumps([
+			["Salary Slip", "docstatus", "=", 1],
+			["Salary Slip", "start_date","Previous","1 month"]
+		]))
+	)
+	number_cards.append(get_number_cards_doc("Salary Structure", "Total Salary Structure",
+		filters_json = json.dumps([
+			["Salary Structure", "docstatus", "=", 1]
+		]))
+	)
+
+	return number_cards
\ No newline at end of file
diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json
new file mode 100644
index 0000000..b5eac46
--- /dev/null
+++ b/erpnext/payroll/desk_page/payroll/payroll.json
@@ -0,0 +1,84 @@
+{
+ "cards": [
+  {
+   "hidden": 0,
+   "label": "Payroll",
+   "links": "[\n    {\n        \"label\": \"Salary Component\",\n        \"name\": \"Salary Component\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Salary Structure\",\n        \"name\": \"Salary Structure\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Salary Structure Assignment\",\n        \"name\": \"Salary Structure Assignment\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Payroll Entry\",\n        \"name\": \"Payroll Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Salary Slip\",\n        \"name\": \"Salary Slip\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Taxation",
+   "links": "[\n    {\n        \"label\": \"Payroll Period\",\n        \"name\": \"Payroll Period\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Income Tax Slab\",\n        \"name\": \"Income Tax Slab\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Employee Tax Exemption Declaration\",\n        \"name\": \"Employee Tax Exemption Declaration\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Employee Tax Exemption Proof Submission\",\n        \"name\": \"Employee Tax Exemption Proof Submission\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Employee Tax Exemption Category\",\n        \"name\": \"Employee Tax Exemption Category\",\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Employee Tax Exemption Sub Category\",\n        \"name\": \"Employee Tax Exemption Sub Category\",\n        \"type\": \"doctype\"\n        \n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Compensations",
+   "links": "[\n    {\n        \"label\": \"Additional Salary\",\n        \"name\": \"Additional Salary\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n        \n    },\n    {\n        \"label\": \"Retention Bonus\",\n        \"name\": \"Retention Bonus\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Employee Incentive\",\n        \"name\": \"Employee Incentive\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Employee Benefit Application\",\n        \"name\": \"Employee Benefit Application\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Employee Benefit Claim\",\n        \"name\": \"Employee Benefit Claim\",\n        \"type\": \"doctype\"\n    }\n]"
+  },
+  {
+   "hidden": 0,
+   "label": "Reports",
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"is_query_report\": true,\n        \"label\": \"Salary Register\",\n        \"name\": \"Salary Register\",\n        \"type\": \"report\"\n        \n    },\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"label\": \"Salary Payments Based On Payment Mode\",\n        \"is_query_report\": true,\n        \"name\": \"Salary Payments Based On Payment Mode\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"label\": \"Salary Payments via ECS\",\n        \"is_query_report\": true,\n        \"name\": \"Salary Payments via ECS\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"label\": \"Income Tax Deductions\",\n        \"is_query_report\": true,\n        \"name\": \"Income Tax Deductions\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"label\": \"Professional Tax Deductions\",\n        \"is_query_report\": true,\n        \"name\": \"Professional Tax Deductions\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Salary Slip\"\n        ],\n        \"doctype\": \"Salary Slip\",\n        \"label\": \"Provident Fund Deductions\",\n        \"is_query_report\": true,\n        \"name\": \"Provident Fund Deductions\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Payroll Entry\"\n        ],\n        \"doctype\": \"Payroll Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Bank Remittance\",\n        \"name\": \"Bank Remittance\",\n        \"type\": \"report\"\n        \n    }\n]"
+  }
+ ],
+ "category": "Modules",
+ "charts": [
+  {
+   "chart_name": "Outgoing Salary",
+   "label": "Outgoing Salary"
+  }
+ ],
+ "creation": "2020-05-27 19:54:23.405607",
+ "developer_mode_only": 0,
+ "disable_user_customization": 0,
+ "docstatus": 0,
+ "doctype": "Desk Page",
+ "extends_another_page": 0,
+ "hide_custom": 0,
+ "idx": 0,
+ "is_standard": 1,
+ "label": "Payroll",
+ "modified": "2020-06-19 12:23:06.034046",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll",
+ "onboarding": "Payroll",
+ "owner": "Administrator",
+ "pin_to_bottom": 0,
+ "pin_to_top": 0,
+ "shortcuts": [
+  {
+   "label": "Salary Structure",
+   "link_to": "Salary Structure",
+   "type": "DocType"
+  },
+  {
+   "label": "Payroll Entry",
+   "link_to": "Payroll Entry",
+   "type": "DocType"
+  },
+  {
+   "color": "",
+   "format": "{} Pending",
+   "label": "Salary Slip",
+   "link_to": "Salary Slip",
+   "stats_filter": "{\"status\": \"Draft\"}",
+   "type": "DocType"
+  },
+  {
+   "label": "Income Tax Slab",
+   "link_to": "Income Tax Slab",
+   "type": "DocType"
+  },
+  {
+   "label": "Salary Register",
+   "link_to": "Salary Register",
+   "type": "Report"
+  },
+  {
+   "label": "Dashboard",
+   "link_to": "Payroll",
+   "type": "Dashboard"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/__init__.py b/erpnext/payroll/doctype/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/__init__.py
diff --git a/erpnext/payroll/doctype/additional_salary/__init__.py b/erpnext/payroll/doctype/additional_salary/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/__init__.py
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js
new file mode 100644
index 0000000..fb42b6f
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Additional Salary', {
+	setup: function(frm) {
+		frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
+
+		frm.set_query("employee", function() {
+			return {
+				filters: {
+					company: frm.doc.company,
+					status:  "Active"
+				}
+			};
+		});
+	},
+});
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json
new file mode 100644
index 0000000..ad64289
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json
@@ -0,0 +1,205 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2018-05-10 12:04:08.396461",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "naming_series",
+  "employee",
+  "employee_name",
+  "salary_component",
+  "overwrite_salary_structure_amount",
+  "deduct_full_tax_on_selected_payroll_date",
+  "ref_doctype",
+  "ref_docname",
+  "column_break_5",
+  "company",
+  "is_recurring",
+  "from_date",
+  "to_date",
+  "payroll_date",
+  "type",
+  "department",
+  "amount",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Series",
+   "options": "HR-ADS-.YY.-.MM.-",
+   "reqd": 1
+  },
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "salary_component",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Salary Component",
+   "options": "Salary Component",
+   "reqd": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "overwrite_salary_structure_amount",
+   "fieldtype": "Check",
+   "label": "Overwrite Salary Structure Amount"
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_full_tax_on_selected_payroll_date",
+   "fieldtype": "Check",
+   "label": "Deduct Full Tax on Selected Payroll Date"
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:(doc.is_recurring==0)",
+   "description": "Date on which this component is applied",
+   "fieldname": "payroll_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Payroll Date",
+   "mandatory_depends_on": "eval:(doc.is_recurring==0)",
+   "search_index": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "salary_component.type",
+   "fieldname": "type",
+   "fieldtype": "Data",
+   "label": "Salary Component Type",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Additional Salary",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "is_recurring",
+   "fieldtype": "Check",
+   "label": "Is Recurring"
+  },
+  {
+   "depends_on": "eval:(doc.is_recurring==1)",
+   "fieldname": "from_date",
+   "fieldtype": "Date",
+   "label": "From Date",
+   "mandatory_depends_on": "eval:(doc.is_recurring==1)"
+  },
+  {
+   "depends_on": "eval:(doc.is_recurring==1)",
+   "fieldname": "to_date",
+   "fieldtype": "Date",
+   "label": "To Date",
+   "mandatory_depends_on": "eval:(doc.is_recurring==1)"
+  },
+  {
+   "fieldname": "ref_doctype",
+   "fieldtype": "Link",
+   "label": "Reference Document Type",
+   "options": "DocType",
+   "read_only": 1
+  },
+  {
+   "fieldname": "ref_docname",
+   "fieldtype": "Dynamic Link",
+   "label": "Reference Document",
+   "options": "ref_doctype",
+   "read_only": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 21:10:50.374063",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Additional Salary",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
new file mode 100644
index 0000000..e369ba7
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import getdate, date_diff
+
+class AdditionalSalary(Document):
+
+	def on_submit(self):
+		if self.ref_doctype == "Employee Advance" and self.ref_docname:
+			frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
+
+	def before_insert(self):
+		if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
+			"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company, "docstatus": 1}):
+
+			frappe.throw(_("Additional Salary Component Exists."))
+
+	def validate(self):
+		self.validate_dates()
+		if self.amount < 0:
+			frappe.throw(_("Amount should not be less than zero."))
+
+	def validate_dates(self):
+		date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee,
+			["date_of_joining", "relieving_date"])
+
+		if getdate(self.from_date) > getdate(self.to_date):
+			frappe.throw(_("From Date can not be greater than To Date."))
+
+		if date_of_joining:
+			if getdate(self.payroll_date) < getdate(date_of_joining):
+				frappe.throw(_("Payroll date can not be less than employee's joining date."))
+			elif getdate(self.from_date) < getdate(date_of_joining):
+				frappe.throw(_("From date can not be less than employee's joining date."))
+			elif relieving_date and getdate(self.to_date) > getdate(relieving_date):
+				frappe.throw(_("To date can not be greater than employee's relieving date."))
+
+	def get_amount(self, sal_start_date, sal_end_date):
+		start_date = getdate(sal_start_date)
+		end_date = getdate(sal_end_date)
+		total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1
+		amount_per_day = self.amount / total_days
+		if getdate(sal_start_date) <= getdate(self.from_date):
+			start_date = getdate(self.from_date)
+		if getdate(sal_end_date) > getdate(self.to_date):
+			end_date = getdate(self.to_date)
+		no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
+		return amount_per_day * no_of_days
+
+@frappe.whitelist()
+def get_additional_salary_component(employee, start_date, end_date, component_type):
+	additional_salaries = frappe.db.sql("""
+		select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
+		from `tabAdditional Salary`
+		where employee=%(employee)s
+			and docstatus = 1
+			and (
+					payroll_date between %(from_date)s and %(to_date)s
+				or
+					from_date <= %(to_date)s and to_date >= %(to_date)s
+				)
+		and type = %(component_type)s
+		order by salary_component, overwrite_salary_structure_amount DESC
+	""", {
+		'employee': employee,
+		'from_date': start_date,
+		'to_date': end_date,
+		'component_type': "Earning" if component_type == "earnings" else "Deduction"
+	}, as_dict=1)
+
+	existing_salary_components= []
+	salary_components_details = {}
+	additional_salary_details = []
+
+	overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
+
+	component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
+	for d in additional_salaries:
+
+		if d.salary_component not in existing_salary_components:
+			component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
+			struct_row = frappe._dict({'salary_component': d.salary_component})
+			if component:
+				struct_row.update(component[0])
+
+			struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
+			struct_row['is_additional_component'] = 1
+
+			salary_components_details[d.salary_component] = struct_row
+
+
+		if overwrites_components.count(d.salary_component) > 1:
+			frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component: {0} between {1} and {2}.".format(d.salary_component, start_date, end_date)), title=_("Error"))
+		else:
+			additional_salary_details.append({
+				'name': d.name,
+				'component': d.salary_component,
+				'amount': d.amount,
+				'type': d.type,
+				'overwrite': d.overwrite_salary_structure_amount,
+			})
+
+		existing_salary_components.append(d.salary_component)
+
+	return salary_components_details, additional_salary_details
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.js b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
new file mode 100644
index 0000000..c18e187
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Additional Salary", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Additional Salary
+		() => frappe.tests.make('Additional Salary', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
new file mode 100644
index 0000000..de26543
--- /dev/null
+++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+import frappe, erpnext
+from frappe.utils import nowdate, add_days
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test
+
+
+class TestAdditionalSalary(unittest.TestCase):
+
+	def setUp(self):
+		setup_test()
+
+	def test_recurring_additional_salary(self):
+		emp_id = make_employee("test_additional@salary.com")
+		frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800))
+		add_sal = get_additional_salary(emp_id)
+
+		ss = make_employee_salary_slip("test_additional@salary.com", "Monthly")
+		for earning in ss.earnings:
+			if earning.salary_component == "Recurring Salary Component":
+				amount = earning.amount
+				salary_component = earning.salary_component
+
+		self.assertEqual(amount, add_sal.amount)
+		self.assertEqual(salary_component, add_sal.salary_component)
+
+
+
+def get_additional_salary(emp_id):
+	create_salary_component("Recurring Salary Component")
+	add_sal = frappe.new_doc("Additional Salary")
+	add_sal.employee = emp_id
+	add_sal.salary_component = "Recurring Salary Component"
+	add_sal.is_recurring = 1
+	add_sal.from_date = add_days(nowdate(), -50)
+	add_sal.to_date = add_days(nowdate(), 180)
+	add_sal.amount = 5000
+	add_sal.save()
+	add_sal.submit()
+
+	return add_sal
diff --git a/erpnext/payroll/doctype/employee_benefit_application/__init__.py b/erpnext/payroll/doctype/employee_benefit_application/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/__init__.py
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js
new file mode 100644
index 0000000..f509df3
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js
@@ -0,0 +1,97 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Benefit Application', {
+	employee: function(frm) {
+		frm.trigger('set_earning_component');
+		var method, args;
+		if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
+			method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
+			args = {
+				employee: frm.doc.employee,
+				on_date: frm.doc.date,
+				payroll_period: frm.doc.payroll_period
+			};
+			get_max_benefits(frm, method, args);
+		}
+		else if(frm.doc.employee && frm.doc.date){
+			method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits";
+			args = {
+				employee: frm.doc.employee,
+				on_date: frm.doc.date
+			};
+			get_max_benefits(frm, method, args);
+		}
+	},
+
+	date: function(frm) {
+		frm.trigger('set_earning_component');
+	},
+
+	set_earning_component: function(frm) {
+		if(!frm.doc.employee && !frm.doc.date) return;
+		frm.set_query("earning_component", "employee_benefits", function() {
+			return {
+				query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
+				filters: {date: frm.doc.date, employee: frm.doc.employee}
+			};
+		});
+	},
+
+	payroll_period: function(frm) {
+		var method, args;
+		if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
+			method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
+			args = {
+				employee: frm.doc.employee,
+				on_date: frm.doc.date,
+				payroll_period: frm.doc.payroll_period
+			};
+			get_max_benefits(frm, method, args);
+		}
+	},
+	max_benefits: function(frm) {
+		calculate_all(frm.doc);
+	}
+});
+
+var get_max_benefits=function(frm, method, args) {
+	frappe.call({
+		method: method,
+		args: args,
+		callback: function (data) {
+			if(!data.exc){
+				if(data.message){
+					frm.set_value("max_benefits", data.message);
+				}
+			}
+		}
+	});
+};
+
+frappe.ui.form.on("Employee Benefit Application Detail",{
+	amount:  function(frm) {
+		calculate_all(frm.doc);
+	},
+	employee_benefits_remove: function(frm) {
+		calculate_all(frm.doc);
+	}
+});
+
+var calculate_all = function(doc) {
+	var tbl = doc.employee_benefits || [];
+	var pro_rata_dispensed_amount = 0;
+	var total_amount = 0;
+	for(var i = 0; i < tbl.length; i++){
+		if(cint(tbl[i].amount) > 0) {
+			total_amount += flt(tbl[i].amount);
+		}
+		if(tbl[i].pay_against_benefit_claim != 1){
+			pro_rata_dispensed_amount += flt(tbl[i].amount);
+		}
+	}
+	doc.total_amount = total_amount;
+	doc.remaining_benefit = doc.max_benefits - total_amount;
+	doc.pro_rata_dispensed_amount = pro_rata_dispensed_amount;
+	refresh_many(['pro_rata_dispensed_amount', 'total_amount','remaining_benefit']);
+};
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
new file mode 100644
index 0000000..275c855
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json
@@ -0,0 +1,191 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "HR-BEN-APP-.YY.-.MM.-.#####",
+ "creation": "2018-04-13 16:31:39.190787",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "max_benefits",
+  "remaining_benefit",
+  "column_break_2",
+  "date",
+  "payroll_period",
+  "department",
+  "amended_from",
+  "section_break_4",
+  "employee_benefits",
+  "totals",
+  "total_amount",
+  "pro_rata_dispensed_amount"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "max_benefits",
+   "fieldtype": "Currency",
+   "label": "Max Benefits (Yearly)",
+   "read_only": 1
+  },
+  {
+   "fieldname": "remaining_benefit",
+   "fieldtype": "Currency",
+   "label": "Remaining Benefits (Yearly)",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "Today",
+   "fieldname": "date",
+   "fieldtype": "Date",
+   "label": "Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payroll_period",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Payroll Period",
+   "options": "Payroll Period",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Benefit Application",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_4",
+   "fieldtype": "Section Break",
+   "label": "Benefits Applied"
+  },
+  {
+   "fieldname": "employee_benefits",
+   "fieldtype": "Table",
+   "label": "Employee Benefits",
+   "options": "Employee Benefit Application Detail",
+   "reqd": 1
+  },
+  {
+   "fieldname": "totals",
+   "fieldtype": "Section Break",
+   "label": "Totals"
+  },
+  {
+   "fieldname": "total_amount",
+   "fieldtype": "Currency",
+   "label": "Total Amount",
+   "read_only": 1
+  },
+  {
+   "fieldname": "pro_rata_dispensed_amount",
+   "fieldtype": "Currency",
+   "label": "Dispensed Amount (Pro-rated)",
+   "read_only": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:58:31.271922",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Benefit Application",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
new file mode 100644
index 0000000..e166a70
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py
@@ -0,0 +1,241 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
+from frappe.model.document import Document
+from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
+from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
+from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
+
+class EmployeeBenefitApplication(Document):
+	def validate(self):
+		self.validate_duplicate_on_payroll_period()
+		if not self.max_benefits:
+			self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)
+		if self.max_benefits and self.max_benefits > 0:
+			self.validate_max_benefit_for_component()
+			self.validate_prev_benefit_claim()
+			if self.remaining_benefit > 0:
+				self.validate_remaining_benefit_amount()
+		else:
+			frappe.throw(_("As per your assigned Salary Structure you cannot apply for benefits").format(self.employee))
+
+	def validate_prev_benefit_claim(self):
+		if self.employee_benefits:
+			for benefit in self.employee_benefits:
+				if benefit.pay_against_benefit_claim == 1:
+					payroll_period = frappe.get_doc("Payroll Period", self.payroll_period)
+					benefit_claimed = get_previous_claimed_amount(self.employee, payroll_period, component = benefit.earning_component)
+					benefit_given = get_sal_slip_total_benefit_given(self.employee, payroll_period, component = benefit.earning_component)
+					benefit_claim_remining = benefit_claimed - benefit_given
+					if benefit_claimed > 0 and benefit_claim_remining > benefit.amount:
+						frappe.throw(_("An amount of {0} already claimed for the component {1},\
+						 set the amount equal or greater than {2}").format(benefit_claimed, benefit.earning_component, benefit_claim_remining))
+
+	def validate_remaining_benefit_amount(self):
+		# check salary structure earnings have flexi component (sum of max_benefit_amount)
+		# without pro-rata which satisfy the remaining_benefit
+		# else pro-rata component for the amount
+		# again comes the same validation and satisfy or throw
+		benefit_components = []
+		if self.employee_benefits:
+			for employee_benefit in self.employee_benefits:
+				benefit_components.append(employee_benefit.earning_component)
+		salary_struct_name = get_assigned_salary_structure(self.employee, self.date)
+		if salary_struct_name:
+			non_pro_rata_amount = 0
+			pro_rata_amount = 0
+			salary_structure = frappe.get_doc("Salary Structure", salary_struct_name)
+			if salary_structure.earnings:
+				for earnings in salary_structure.earnings:
+					if earnings.is_flexible_benefit == 1 and earnings.salary_component not in benefit_components:
+						pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", earnings.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+						if pay_against_benefit_claim != 1:
+							pro_rata_amount += max_benefit_amount
+						else:
+							non_pro_rata_amount += max_benefit_amount
+
+			if pro_rata_amount == 0  and non_pro_rata_amount == 0:
+				frappe.throw(_("Please add the remaining benefits {0} to any of the existing component").format(self.remaining_benefit))
+			elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remaining_benefit):
+				frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \
+				as pro-rata component").format(non_pro_rata_amount, self.remaining_benefit - non_pro_rata_amount))
+			elif non_pro_rata_amount == 0:
+				frappe.throw(_("Please add the remaining benefits {0} to the application as \
+				pro-rata component").format(self.remaining_benefit))
+
+	def validate_max_benefit_for_component(self):
+		if self.employee_benefits:
+			max_benefit_amount = 0
+			for employee_benefit in self.employee_benefits:
+				self.validate_max_benefit(employee_benefit.earning_component)
+				max_benefit_amount += employee_benefit.amount
+			if max_benefit_amount > self.max_benefits:
+				frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, self.max_benefits))
+
+	def validate_max_benefit(self, earning_component_name):
+		max_benefit_amount = frappe.db.get_value("Salary Component", earning_component_name, "max_benefit_amount")
+		benefit_amount = 0
+		for employee_benefit in self.employee_benefits:
+			if employee_benefit.earning_component == earning_component_name:
+				benefit_amount += employee_benefit.amount
+		prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name)
+		benefit_amount += prev_sal_slip_flexi_amount
+		if rounded(benefit_amount, 2) > max_benefit_amount:
+			frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount))
+
+	def validate_duplicate_on_payroll_period(self):
+		application = frappe.db.exists(
+			"Employee Benefit Application",
+			{
+				'employee': self.employee,
+				'payroll_period': self.payroll_period,
+				'docstatus': 1
+			}
+		)
+		if application:
+			frappe.throw(_("Employee {0} already submited an apllication {1} for the payroll period {2}").format(self.employee, application, self.payroll_period))
+
+@frappe.whitelist()
+def get_max_benefits(employee, on_date):
+	sal_struct = get_assigned_salary_structure(employee, on_date)
+	if sal_struct:
+		max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits")
+		if max_benefits > 0:
+			return max_benefits
+	return False
+
+@frappe.whitelist()
+def get_max_benefits_remaining(employee, on_date, payroll_period):
+	max_benefits = get_max_benefits(employee, on_date)
+	if max_benefits and max_benefits > 0:
+		have_depends_on_payment_days = False
+		per_day_amount_total = 0
+		payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0]
+		payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
+
+		# Get all salary slip flexi amount in the payroll period
+		prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj)
+
+		if prev_sal_slip_flexi_total > 0:
+			# Check salary structure hold depends_on_payment_days component
+			# If yes then find the amount per day of each component and find the sum
+			sal_struct_name = get_assigned_salary_structure(employee, on_date)
+			if sal_struct_name:
+				sal_struct = frappe.get_doc("Salary Structure", sal_struct_name)
+				for sal_struct_row in sal_struct.get("earnings"):
+					salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
+					if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
+						have_depends_on_payment_days = True
+						benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
+						amount_per_day = benefit_amount / payroll_period_days
+						per_day_amount_total += amount_per_day
+
+			# Then the sum multiply with the no of lwp in that period
+			# Include that amount to the prev_sal_slip_flexi_total to get the actual
+			if have_depends_on_payment_days and per_day_amount_total > 0:
+				holidays = get_holidays_for_employee(employee, payroll_period_obj.start_date, on_date)
+				working_days = date_diff(on_date, payroll_period_obj.start_date) + 1
+				leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days)
+				leave_days_amount = leave_days * per_day_amount_total
+				prev_sal_slip_flexi_total += leave_days_amount
+
+			return max_benefits - prev_sal_slip_flexi_total
+	return max_benefits
+
+def calculate_lwp(employee, start_date, holidays, working_days):
+	lwp = 0
+	holidays = "','".join(holidays)
+	for d in range(working_days):
+		dt = add_days(cstr(getdate(start_date)), d)
+		leave = frappe.db.sql("""
+			select t1.name, t1.half_day
+			from `tabLeave Application` t1, `tabLeave Type` t2
+			where t2.name = t1.leave_type
+			and t2.is_lwp = 1
+			and t1.docstatus = 1
+			and t1.employee = %(employee)s
+			and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
+			WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
+			END
+			""".format(holidays), {"employee": employee, "dt": dt})
+		if leave:
+			lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
+	return lwp
+
+def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
+	if not payroll_period:
+		frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
+			.format(salary_component))
+		return False
+
+	# Considering there is only one application for a year
+	benefit_application = frappe.db.sql("""
+		select name
+		from `tabEmployee Benefit Application`
+		where
+			payroll_period=%(payroll_period)s
+			and employee=%(employee)s
+			and docstatus = 1
+	""", {
+		'employee': employee,
+		'payroll_period': payroll_period.name
+	})
+
+	current_benefit_amount = 0.0
+	component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
+		salary_component, ["max_benefit_amount", "depends_on_payment_days"])
+
+	benefit_amount = 0
+	if benefit_application:
+		benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
+			{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
+	elif component_max_benefit:
+		benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
+
+	current_benefit_amount = 0
+	if benefit_amount:
+		total_sub_periods = get_period_factor(employee,
+			start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
+
+		current_benefit_amount = benefit_amount / total_sub_periods
+
+	return current_benefit_amount
+
+def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
+	max_benefits_total = 0
+	benefit_amount = 0
+	for d in sal_struct.get("earnings"):
+		if d.is_flexible_benefit == 1:
+			component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
+			if not component.pay_against_benefit_claim:
+				max_benefits_total += component.max_benefit_amount
+
+	if max_benefits_total > 0:
+		benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total
+		if benefit_amount > component_max_benefit:
+			benefit_amount = component_max_benefit
+
+	return benefit_amount
+
+
+def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
+	if len(filters) < 2:
+		return {}
+
+	salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
+
+	if salary_structure:
+		return frappe.db.sql("""
+			select salary_component
+			from `tabSalary Detail`
+			where parent = %s and is_flexible_benefit = 1
+			order by name
+		""", salary_structure)
+	else:
+		frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
+			.format(filters['employee'], filters['date']))
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
new file mode 100644
index 0000000..b355e1c
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Benefit Application", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Benefit Application
+		() => frappe.tests.make('Employee Benefit Application', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py
new file mode 100644
index 0000000..34e1a8f
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+
+class TestEmployeeBenefitApplication(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py b/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py
diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json
new file mode 100644
index 0000000..f0415d2
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json
@@ -0,0 +1,58 @@
+{
+ "actions": [],
+ "creation": "2018-04-13 16:36:18.389786",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "earning_component",
+  "pay_against_benefit_claim",
+  "max_benefit_amount",
+  "amount"
+ ],
+ "fields": [
+  {
+   "fieldname": "earning_component",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Earning Component",
+   "options": "Salary Component",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "earning_component.pay_against_benefit_claim",
+   "fieldname": "pay_against_benefit_claim",
+   "fieldtype": "Check",
+   "label": "Pay Against Benefit Claim",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "earning_component.max_benefit_amount",
+   "fieldname": "max_benefit_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Max Benefit Amount",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:45:00.519134",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Benefit Application Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py
new file mode 100644
index 0000000..65405fe
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class EmployeeBenefitApplicationDetail(Document):
+	pass
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/__init__.py b/erpnext/payroll/doctype/employee_benefit_claim/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/__init__.py
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js
new file mode 100644
index 0000000..6db6cb8
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Benefit Claim', {
+	setup: function(frm) {
+		frm.set_query("earning_component", function() {
+			return {
+				query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
+				filters: {date: frm.doc.claim_date, employee: frm.doc.employee}
+			};
+		});
+	},
+	employee: function(frm) {
+		frm.set_value("earning_component", null);
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json
new file mode 100644
index 0000000..7fea59f
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json
@@ -0,0 +1,194 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####",
+ "creation": "2018-04-13 16:43:10.386409",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "department",
+  "column_break_3",
+  "claim_date",
+  "benefit_type_and_amount",
+  "earning_component",
+  "max_amount_eligible",
+  "pay_against_benefit_claim",
+  "claimed_amount",
+  "salary_slip",
+  "amended_from",
+  "section_break_9",
+  "attachments"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 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
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "Today",
+   "fieldname": "claim_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Claim Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "benefit_type_and_amount",
+   "fieldtype": "Section Break",
+   "label": "Benefit Type and Amount"
+  },
+  {
+   "fieldname": "earning_component",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Claim Benefit For",
+   "options": "Salary Component",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "earning_component.max_benefit_amount",
+   "fieldname": "max_amount_eligible",
+   "fieldtype": "Currency",
+   "label": "Max Amount Eligible",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "earning_component.pay_against_benefit_claim",
+   "fieldname": "pay_against_benefit_claim",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Pay Against Benefit Claim",
+   "read_only": 1
+  },
+  {
+   "fieldname": "claimed_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Claimed Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "salary_slip",
+   "fieldtype": "Link",
+   "label": "Salary Slip",
+   "options": "Salary Slip",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Benefit Claim",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_9",
+   "fieldtype": "Section Break",
+   "label": "Expense Proof"
+  },
+  {
+   "fieldname": "attachments",
+   "fieldtype": "Attach",
+   "label": "Attachments"
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:01:50.791676",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Benefit Claim",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
new file mode 100644
index 0000000..d9937a7
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import flt
+from frappe.model.document import Document
+from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
+from erpnext.hr.utils import get_previous_claimed_amount
+from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period
+from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
+
+class EmployeeBenefitClaim(Document):
+	def validate(self):
+		max_benefits = get_max_benefits(self.employee, self.claim_date)
+		if not max_benefits or max_benefits <= 0:
+			frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
+		payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company"))
+		if not payroll_period:
+			frappe.throw(_("{0} is not in a valid Payroll Period").format(frappe.format(self.claim_date, dict(fieldtype='Date'))))
+		self.validate_max_benefit_for_component(payroll_period)
+		self.validate_max_benefit_for_sal_struct(max_benefits)
+		self.validate_benefit_claim_amount(max_benefits, payroll_period)
+		if self.pay_against_benefit_claim:
+			self.validate_non_pro_rata_benefit_claim(max_benefits, payroll_period)
+
+	def validate_benefit_claim_amount(self, max_benefits, payroll_period):
+		claimed_amount = self.claimed_amount
+		claimed_amount += get_previous_claimed_amount(self.employee, payroll_period)
+		if max_benefits < claimed_amount:
+			frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of previous claimed\
+			amount").format(self.employee, max_benefits, claimed_amount-max_benefits))
+
+	def validate_max_benefit_for_sal_struct(self, max_benefits):
+		if self.claimed_amount > max_benefits:
+			frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits))
+
+	def validate_max_benefit_for_component(self, payroll_period):
+		if self.max_amount_eligible:
+			claimed_amount = self.claimed_amount
+			claimed_amount += get_previous_claimed_amount(self.employee,
+				payroll_period, component = self.earning_component)
+			if claimed_amount > self.max_amount_eligible:
+				frappe.throw(_("Maximum amount eligible for the component {0} exceeds {1}")
+					.format(self.earning_component, self.max_amount_eligible))
+
+	def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period):
+		claimed_amount = self.claimed_amount
+		pro_rata_amount = self.get_pro_rata_amount_in_application(payroll_period.name)
+		if not pro_rata_amount:
+			pro_rata_amount = 0
+			# Get pro_rata_amount if there is no application,
+			# get salary structure for the date and calculate pro-rata amount
+			sal_struct_name = get_assigned_salary_structure(self.employee, self.claim_date)
+			if sal_struct_name:
+				sal_struct = frappe.get_doc("Salary Structure", sal_struct_name)
+				pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct)
+
+		claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata = True)
+		if max_benefits < pro_rata_amount + claimed_amount:
+			frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\
+			amount and previous claimed amount").format(self.employee, max_benefits, pro_rata_amount+claimed_amount-max_benefits))
+
+	def get_pro_rata_amount_in_application(self, payroll_period):
+		application = frappe.db.exists(
+			"Employee Benefit Application",
+			{
+				'employee': self.employee,
+				'payroll_period': payroll_period,
+				'docstatus': 1
+			}
+		)
+		if application:
+			return frappe.db.get_value("Employee Benefit Application", application, "pro_rata_dispensed_amount")
+		return False
+
+def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
+	total_pro_rata_max = 0
+	benefit_amount_total = 0
+	for sal_struct_row in sal_struct.get("earnings"):
+		try:
+			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+		except TypeError:
+			# show the error in tests?
+			frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component))
+		if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
+			total_pro_rata_max += max_benefit_amount
+	if total_pro_rata_max > 0:
+		for sal_struct_row in sal_struct.get("earnings"):
+			pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
+
+			if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
+				component_max = max_benefit_amount
+				benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max
+				if benefit_amount > component_max:
+					benefit_amount = component_max
+				benefit_amount_total += benefit_amount
+	return benefit_amount_total
+
+def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
+	query = """
+		select sum(claimed_amount)
+		from `tabEmployee Benefit Claim`
+		where
+			employee=%(employee)s
+			and docstatus = 1
+			and pay_against_benefit_claim = 1
+			and claim_date between %(start_date)s and %(end_date)s
+	"""
+
+	if salary_component:
+		query += " and earning_component = %(earning_component)s"
+
+	claimed_amount = flt(frappe.db.sql(query, {
+		'employee': employee,
+		'start_date': start_date,
+		'end_date': end_date,
+		'earning_component': salary_component
+	})[0][0])
+
+	return claimed_amount
+
+def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
+	pro_rata_amount = 0
+	claimed_amount = 0
+	application = frappe.db.exists(
+		"Employee Benefit Application",
+		{
+			'employee': employee,
+			'payroll_period': payroll_period.name,
+			'docstatus': 1
+		}
+	)
+	if application:
+		application_obj = frappe.get_doc("Employee Benefit Application", application)
+		pro_rata_amount = application_obj.pro_rata_dispensed_amount + application_obj.max_benefits - application_obj.remaining_benefit
+	else:
+		pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
+
+	claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
+
+	return claimed_amount + pro_rata_amount
+
+def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period,  sal_struct):
+	max_benefits = get_max_benefits(employee, payroll_period.end_date)
+	if not max_benefits:
+		max_benefits = 0
+	remaining_benefit = max_benefits - get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period)
+	if remaining_benefit > 0:
+		have_remaining = True
+		# Set the remaining benefits to flexi non pro-rata component in the salary structure
+		salary_components_array = []
+		for d in sal_struct.get("earnings"):
+			if d.is_flexible_benefit == 1:
+				salary_component = frappe.get_doc("Salary Component", d.salary_component)
+				if salary_component.pay_against_benefit_claim == 1:
+					claimed_amount = get_benefit_claim_amount(employee, payroll_period.start_date, sal_slip_end_date, d.salary_component)
+					amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount
+					if amount_fit_to_component > 0:
+						if remaining_benefit > amount_fit_to_component:
+							amount = amount_fit_to_component
+							remaining_benefit -= amount_fit_to_component
+						else:
+							amount = remaining_benefit
+							have_remaining = False
+						current_claimed_amount = get_benefit_claim_amount(employee, sal_slip_start_date, sal_slip_end_date, d.salary_component)
+						amount += current_claimed_amount
+						struct_row = {}
+						salary_components_dict = {}
+						struct_row['depends_on_payment_days'] = salary_component.depends_on_payment_days
+						struct_row['salary_component'] = salary_component.name
+						struct_row['abbr'] = salary_component.salary_component_abbr
+						struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total
+						struct_row['is_tax_applicable'] = salary_component.is_tax_applicable,
+						struct_row['is_flexible_benefit'] = salary_component.is_flexible_benefit,
+						struct_row['variable_based_on_taxable_salary'] = salary_component.variable_based_on_taxable_salary
+						salary_components_dict['amount'] = amount
+						salary_components_dict['struct_row'] = struct_row
+						salary_components_array.append(salary_components_dict)
+			if not have_remaining:
+				break
+
+		if len(salary_components_array) > 0:
+			return salary_components_array
+
+	return False
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
new file mode 100644
index 0000000..3c808c0
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Benefit Claim", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Benefit Claim
+		() => frappe.tests.make('Employee Benefit Claim', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py
new file mode 100644
index 0000000..aff73e5
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+
+class TestEmployeeBenefitClaim(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/employee_incentive/__init__.py b/erpnext/payroll/doctype/employee_incentive/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/__init__.py
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js
new file mode 100644
index 0000000..db0f83a
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Incentive', {
+	setup: function(frm) {
+		frm.set_query("employee", function() {
+			return {
+				filters: {
+					"status": "Active"
+				}
+			};
+		});
+
+		frm.set_query("salary_component", function() {
+			return {
+				filters: {
+					"type": "Earning"
+				}
+			};
+		});
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json
new file mode 100644
index 0000000..81ff865
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json
@@ -0,0 +1,123 @@
+{
+ "actions": [],
+ "autoname": "HR-EINV-.YY.-.MM.-.#####",
+ "creation": "2018-04-13 16:13:43.404546",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "incentive_amount",
+  "employee_name",
+  "salary_component",
+  "column_break_5",
+  "payroll_date",
+  "department",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fieldname": "incentive_amount",
+   "fieldtype": "Currency",
+   "label": "Incentive Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payroll_date",
+   "fieldtype": "Date",
+   "label": "Payroll Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Incentive",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "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
+  },
+  {
+   "fieldname": "salary_component",
+   "fieldtype": "Link",
+   "label": "Salary Component",
+   "options": "Salary Component",
+   "reqd": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:42:51.209630",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Incentive",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
new file mode 100644
index 0000000..44763fc
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class EmployeeIncentive(Document):
+	def on_submit(self):
+		company = frappe.db.get_value('Employee', self.employee, 'company')
+
+		additional_salary = frappe.new_doc('Additional Salary')
+		additional_salary.employee = self.employee
+		additional_salary.salary_component = self.salary_component
+		additional_salary.amount = self.incentive_amount
+		additional_salary.payroll_date = self.payroll_date
+		additional_salary.company = company
+		additional_salary.ref_doctype = self.doctype
+		additional_salary.ref_docname = self.name
+		additional_salary.submit()
diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
new file mode 100644
index 0000000..10bc037
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Incentive", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Incentive
+		() => frappe.tests.make('Employee Incentive', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
new file mode 100644
index 0000000..f7597ad
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestEmployeeIncentive(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/employee_other_income/__init__.py b/erpnext/payroll/doctype/employee_other_income/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_other_income/__init__.py
diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.js b/erpnext/payroll/doctype/employee_other_income/employee_other_income.js
new file mode 100644
index 0000000..c1a74e8
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Other Income', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.json b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json
new file mode 100644
index 0000000..c5a2a73
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json
@@ -0,0 +1,135 @@
+{
+ "actions": [],
+ "autoname": "HR-INCOME-.######",
+ "creation": "2020-03-18 15:04:40.767434",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "payroll_period",
+  "column_break_3",
+  "company",
+  "source",
+  "amount",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payroll_period",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Payroll Period",
+   "options": "Payroll Period",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "source",
+   "fieldtype": "Data",
+   "label": "Source"
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "Company:company:default_currency",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Other Income",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:55:17.604688",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Other Income",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
new file mode 100644
index 0000000..ab63c0d
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class EmployeeOtherIncome(Document):
+	pass
diff --git a/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py
new file mode 100644
index 0000000..2eeca7a
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestEmployeeOtherIncome(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js
new file mode 100644
index 0000000..1df609f
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Tax Exemption Category', {
+	refresh: function(frm) {
+
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json
new file mode 100644
index 0000000..c097082
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json
@@ -0,0 +1,74 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2018-04-13 16:51:36.971140",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "max_amount",
+  "is_active"
+ ],
+ "fields": [
+  {
+   "fieldname": "max_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Max Exemption Amount"
+  },
+  {
+   "default": "1",
+   "fieldname": "is_active",
+   "fieldtype": "Check",
+   "label": "Is Active"
+  }
+ ],
+ "links": [],
+ "modified": "2020-05-27 23:16:47.472910",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Category",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py
new file mode 100644
index 0000000..4f705db
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class EmployeeTaxExemptionCategory(Document):
+	pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
new file mode 100644
index 0000000..e0e43c3
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Tax Exemption Category", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Tax Exemption Category
+		() => frappe.tests.make('Employee Tax Exemption Category', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py
new file mode 100644
index 0000000..669fb71
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestEmployeeTaxExemptionCategory(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js
new file mode 100644
index 0000000..0e0c9b5
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js
@@ -0,0 +1,51 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Tax Exemption Declaration', {
+	setup: function(frm) {
+		frm.set_query('employee', function() {
+			return {
+				filters: {
+					'status': "Active"
+				}
+			}
+		});
+
+		frm.set_query('payroll_period', function() {
+			const fields = {'employee': 'Employee', 'company': 'Company'};
+
+			for (let [field, label] of Object.entries(fields)) {
+				if (!frm.doc[field]) {
+					frappe.msgprint(__("Please select {0}", [label]))
+				}
+			};
+
+			if (frm.doc.employee && frm.doc.company){
+				return {
+					filters: {
+						'company': frm.doc.company
+					}
+				}
+			}
+		});
+
+		frm.set_query('exemption_sub_category', 'declarations', function() {
+			return {
+				filters: {
+					'is_active': 1
+				}
+			}
+		});
+	},
+
+	refresh: function(frm) {
+		if(frm.doc.docstatus==1) {
+			frm.add_custom_button(__('Submit Proof'), function() {
+				frappe.model.open_mapped_doc({
+					method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission",
+					frm: frm
+				});
+			}).addClass("btn-primary");
+		}
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json
new file mode 100644
index 0000000..5423365
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json
@@ -0,0 +1,180 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "HR-TAX-DEC-.YYYY.-.#####",
+ "creation": "2018-04-13 16:53:36.175504",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "department",
+  "column_break_2",
+  "payroll_period",
+  "company",
+  "amended_from",
+  "section_break_8",
+  "declarations",
+  "section_break_10",
+  "total_declared_amount",
+  "column_break_12",
+  "total_exemption_amount"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 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
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "payroll_period",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Payroll Period",
+   "options": "Payroll Period",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Tax Exemption Declaration",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "declarations",
+   "fieldtype": "Table",
+   "label": "Declarations",
+   "options": "Employee Tax Exemption Declaration Category"
+  },
+  {
+   "fieldname": "section_break_10",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "total_declared_amount",
+   "fieldtype": "Currency",
+   "label": "Total Declared Amount",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "total_exemption_amount",
+   "fieldtype": "Currency",
+   "label": "Total Exemption Amount",
+   "read_only": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:49:43.829892",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Declaration",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
new file mode 100644
index 0000000..fb71a28
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import flt
+from frappe.model.mapper import get_mapped_doc
+from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
+	calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
+
+class EmployeeTaxExemptionDeclaration(Document):
+	def validate(self):
+		validate_tax_declaration(self.declarations)
+		validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
+		self.set_total_declared_amount()
+		self.set_total_exemption_amount()
+		self.calculate_hra_exemption()
+
+	def set_total_declared_amount(self):
+		self.total_declared_amount = 0.0
+		for d in self.declarations:
+			self.total_declared_amount += flt(d.amount)
+
+	def set_total_exemption_amount(self):
+		self.total_exemption_amount = get_total_exemption_amount(self.declarations)
+
+	def calculate_hra_exemption(self):
+		self.salary_structure_hra, self.annual_hra_exemption, self.monthly_hra_exemption = 0, 0, 0
+		if self.get("monthly_house_rent"):
+			hra_exemption = calculate_annual_eligible_hra_exemption(self)
+			if hra_exemption:
+				self.total_exemption_amount += hra_exemption["annual_exemption"]
+				self.salary_structure_hra = hra_exemption["hra_amount"]
+				self.annual_hra_exemption = hra_exemption["annual_exemption"]
+				self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
+
+@frappe.whitelist()
+def make_proof_submission(source_name, target_doc=None):
+	doclist = get_mapped_doc("Employee Tax Exemption Declaration", source_name, {
+		"Employee Tax Exemption Declaration": {
+			"doctype": "Employee Tax Exemption Proof Submission",
+			"field_no_map": ["monthly_house_rent", "monthly_hra_exemption"]
+		},
+		"Employee Tax Exemption Declaration Category": {
+			"doctype": "Employee Tax Exemption Proof Submission Detail",
+			"add_if_empty": True
+		}
+	}, target_doc)
+
+	return doclist
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
new file mode 100644
index 0000000..274a3a3
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Tax Exemption Declaration", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Tax Exemption Declaration
+		() => frappe.tests.make('Employee Tax Exemption Declaration', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
new file mode 100644
index 0000000..9549fd1
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe, erpnext
+import unittest
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.utils import DuplicateDeclarationError
+
+class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
+	def setUp(self):
+		make_employee("employee@taxexepmtion.com")
+		make_employee("employee1@taxexepmtion.com")
+		create_payroll_period()
+		create_exemption_category()
+		frappe.db.sql("""delete from `tabEmployee Tax Exemption Declaration`""")
+
+	def test_duplicate_category_in_declaration(self):
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Declaration",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
+			"company": erpnext.get_default_company(),
+			"payroll_period": "_Test Payroll Period",
+			"declarations": [
+				dict(exemption_sub_category = "_Test Sub Category",
+					exemption_category = "_Test Category",
+					amount = 100000),
+				dict(exemption_sub_category = "_Test Sub Category",
+					exemption_category = "_Test Category",
+					amount = 50000)
+			]
+		})
+		self.assertRaises(frappe.ValidationError, declaration.save)
+
+	def test_duplicate_entry_for_payroll_period(self):
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Declaration",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
+			"company":  erpnext.get_default_company(),
+			"payroll_period": "_Test Payroll Period",
+			"declarations": [
+				dict(exemption_sub_category = "_Test Sub Category",
+					exemption_category = "_Test Category",
+					amount = 100000),
+				dict(exemption_sub_category = "_Test1 Sub Category",
+					exemption_category = "_Test Category",
+					amount = 50000),
+			]
+		}).insert()
+
+		duplicate_declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Declaration",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
+			"company":  erpnext.get_default_company(),
+			"payroll_period": "_Test Payroll Period",
+			"declarations": [
+				dict(exemption_sub_category = "_Test Sub Category",
+					exemption_category = "_Test Category",
+					amount = 100000)
+			]
+		})
+		self.assertRaises(DuplicateDeclarationError, duplicate_declaration.insert)
+		duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name")
+		self.assertTrue(duplicate_declaration.insert)
+
+	def test_exemption_amount(self):
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Declaration",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
+			"company":  erpnext.get_default_company(),
+			"payroll_period": "_Test Payroll Period",
+			"declarations": [
+				dict(exemption_sub_category = "_Test Sub Category",
+					exemption_category = "_Test Category",
+					amount = 80000),
+				dict(exemption_sub_category = "_Test1 Sub Category",
+					exemption_category = "_Test Category",
+					amount = 60000),
+			]
+		}).insert()
+
+		self.assertEqual(declaration.total_exemption_amount, 100000)
+
+def create_payroll_period():
+	if not frappe.db.exists("Payroll Period", "_Test Payroll Period"):
+		from datetime import date
+		payroll_period = frappe.get_doc(dict(
+			doctype = 'Payroll Period',
+			name = "_Test Payroll Period",
+			company =  erpnext.get_default_company(),
+			start_date = date(date.today().year, 1, 1),
+			end_date = date(date.today().year, 12, 31)
+		)).insert()
+		return payroll_period
+	else:
+		return frappe.get_doc("Payroll Period", "_Test Payroll Period")
+
+def create_exemption_category():
+	if not frappe.db.exists("Employee Tax Exemption Category", "_Test Category"):
+		category = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Category",
+			"name": "_Test Category",
+			"deduction_component": "Income Tax",
+			"max_amount": 100000
+		}).insert()
+	if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"):
+		frappe.get_doc({
+			"doctype": "Employee Tax Exemption Sub Category",
+			"name": "_Test Sub Category",
+			"exemption_category": "_Test Category",
+			"max_amount": 100000,
+			"is_active": 1
+		}).insert()
+	if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"):
+		frappe.get_doc({
+			"doctype": "Employee Tax Exemption Sub Category",
+			"name": "_Test1 Sub Category",
+			"exemption_category": "_Test Category",
+			"max_amount": 50000,
+			"is_active": 1
+		}).insert()
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json
new file mode 100644
index 0000000..eddaec2
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json
@@ -0,0 +1,61 @@
+{
+ "actions": [],
+ "creation": "2018-04-13 16:56:23.333041",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "exemption_sub_category",
+  "exemption_category",
+  "max_amount",
+  "amount"
+ ],
+ "fields": [
+  {
+   "fieldname": "exemption_sub_category",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Exemption Sub Category",
+   "options": "Employee Tax Exemption Sub Category",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "exemption_sub_category.exemption_category",
+   "fieldname": "exemption_category",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Exemption Category",
+   "options": "Employee Tax Exemption Category",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fetch_from": "exemption_sub_category.max_amount",
+   "fieldname": "max_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Maximum Exempted Amount",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Declared Amount",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:41:03.638739",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Declaration Category",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py
new file mode 100644
index 0000000..bff747f
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class EmployeeTaxExemptionDeclarationCategory(Document):
+	pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js
new file mode 100644
index 0000000..715d755
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js
@@ -0,0 +1,58 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Tax Exemption Proof Submission', {
+	setup: function(frm) {
+		frm.set_query('employee', function() {
+			return {
+				filters: {
+					'status': "Active"
+				}
+			}
+		});
+
+		frm.set_query('payroll_period', function() {
+			if(frm.doc.employee && frm.doc.company){
+				return {
+					filters: {
+						'company': frm.doc.company
+					}
+				}
+			}else {
+				frappe.msgprint(__("Please select Employee"));
+			}
+		});
+
+		frm.set_query('exemption_sub_category', 'tax_exemption_proofs', function() {
+			return {
+				filters: {
+					'is_active': 1
+				}
+			}
+		});
+	},
+
+	refresh: function(frm) {
+		if(frm.doc.docstatus === 0) {
+			let filters = {
+				docstatus: 1,
+				company: frm.doc.company
+			};
+			if(frm.doc.employee) filters["employee"] = frm.doc.employee;
+			if(frm.doc.payroll_period) filters["payroll_period"] = frm.doc.payroll_period;
+
+			frm.add_custom_button(__('Get Details From Declaration'), function() {
+				erpnext.utils.map_current_doc({
+					method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission",
+					source_doctype: "Employee Tax Exemption Declaration",
+					target: frm,
+					date_field: "creation",
+					setters: {
+						employee: frm.doc.employee || undefined
+					},
+					get_query_filters: filters
+				});
+			});
+		}
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json
new file mode 100644
index 0000000..de8fa09
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json
@@ -0,0 +1,203 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "HR-TAX-PRF-.YYYY.-.#####",
+ "creation": "2018-04-13 17:24:11.456132",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "department",
+  "column_break_2",
+  "submission_date",
+  "payroll_period",
+  "company",
+  "section_break_5",
+  "tax_exemption_proofs",
+  "section_break_10",
+  "total_actual_amount",
+  "column_break_12",
+  "exemption_amount",
+  "attachment_section",
+  "attachments",
+  "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
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "Today",
+   "fieldname": "submission_date",
+   "fieldtype": "Date",
+   "label": "Submission Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "payroll_period",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Payroll Period",
+   "options": "Payroll Period",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "employee.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "tax_exemption_proofs",
+   "fieldtype": "Table",
+   "label": "Tax Exemption Proofs",
+   "options": "Employee Tax Exemption Proof Submission Detail"
+  },
+  {
+   "fieldname": "section_break_10",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "total_actual_amount",
+   "fieldtype": "Currency",
+   "label": "Total Actual Amount",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "exemption_amount",
+   "fieldtype": "Currency",
+   "label": "Total Exemption Amount",
+   "read_only": 1
+  },
+  {
+   "fieldname": "attachment_section",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "attachments",
+   "fieldtype": "Attach",
+   "label": "Attachments"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Employee Tax Exemption Proof Submission",
+   "print_hide": 1,
+   "read_only": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:53:10.412321",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Proof Submission",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
new file mode 100644
index 0000000..5bc33a6
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import flt
+from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \
+	calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
+
+class EmployeeTaxExemptionProofSubmission(Document):
+	def validate(self):
+		validate_tax_declaration(self.tax_exemption_proofs)
+		self.set_total_actual_amount()
+		self.set_total_exemption_amount()
+		self.calculate_hra_exemption()
+		validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
+
+	def set_total_actual_amount(self):
+		self.total_actual_amount = flt(self.get("house_rent_payment_amount"))
+		for d in self.tax_exemption_proofs:
+			self.total_actual_amount += flt(d.amount)
+
+	def set_total_exemption_amount(self):
+		self.exemption_amount = get_total_exemption_amount(self.tax_exemption_proofs)
+
+	def calculate_hra_exemption(self):
+		self.monthly_hra_exemption, self.monthly_house_rent, self.total_eligible_hra_exemption = 0, 0, 0
+		if self.get("house_rent_payment_amount"):
+			hra_exemption = calculate_hra_exemption_for_period(self)
+			if hra_exemption:
+				self.exemption_amount += hra_exemption["total_eligible_hra_exemption"]
+				self.monthly_hra_exemption = hra_exemption["monthly_exemption"]
+				self.monthly_house_rent = hra_exemption["monthly_house_rent"]
+				self.total_eligible_hra_exemption = hra_exemption["total_eligible_hra_exemption"]
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
new file mode 100644
index 0000000..cec7508
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Tax Exemption Proof Submission", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Tax Exemption Proof Submission
+		() => frappe.tests.make('Employee Tax Exemption Proof Submission', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py
new file mode 100644
index 0000000..cb9ed5f
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period
+
+class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase):
+	def setup(self):
+		make_employee("employee@proofsubmission.com")
+		create_payroll_period()
+		create_exemption_category()
+		frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""")
+
+	def test_exemption_amount_lesser_than_category_max(self):
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Proof Submission",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
+			"payroll_period": "Test Payroll Period",
+			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
+							type_of_proof = "Test Proof",
+							exemption_category = "_Test Category",
+							amount = 150000)]
+		})
+		self.assertRaises(frappe.ValidationError, declaration.save)
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Proof Submission",
+			"payroll_period": "Test Payroll Period",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
+			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
+							type_of_proof = "Test Proof",
+							exemption_category = "_Test Category",
+							amount = 100000)]
+		})
+		self.assertTrue(declaration.save)
+		self.assertTrue(declaration.submit)
+
+	def test_duplicate_category_in_proof_submission(self):
+		declaration = frappe.get_doc({
+			"doctype": "Employee Tax Exemption Proof Submission",
+			"employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"),
+			"payroll_period": "Test Payroll Period",
+			"tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category",
+							exemption_category = "_Test Category",
+							type_of_proof = "Test Proof",
+							amount = 100000),
+							dict(exemption_sub_category = "_Test Sub Category",
+							exemption_category = "_Test Category",
+							amount = 50000),
+							]
+		})
+		self.assertRaises(frappe.ValidationError, declaration.save)
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json
new file mode 100644
index 0000000..4c53bd3
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json
@@ -0,0 +1,66 @@
+{
+ "actions": [],
+ "creation": "2018-04-13 17:19:03.006149",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "exemption_sub_category",
+  "exemption_category",
+  "max_amount",
+  "type_of_proof",
+  "amount"
+ ],
+ "fields": [
+  {
+   "fieldname": "exemption_sub_category",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Exemption Sub Category",
+   "options": "Employee Tax Exemption Sub Category",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "exemption_sub_category.exemption_category",
+   "fieldname": "exemption_category",
+   "fieldtype": "Read Only",
+   "in_list_view": 1,
+   "label": "Exemption Category",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "exemption_sub_category.max_amount",
+   "fieldname": "max_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Maximum Exemption Amount",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "type_of_proof",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Type of Proof",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Actual Amount"
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:37:08.265600",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Proof Submission Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py
new file mode 100644
index 0000000..0244ae6
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class EmployeeTaxExemptionProofSubmissionDetail(Document):
+	pass
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js
new file mode 100644
index 0000000..8a83a27
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Employee Tax Exemption Sub Category', {
+	refresh: function(frm) {
+
+	}
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json
new file mode 100644
index 0000000..b89d9c1
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json
@@ -0,0 +1,86 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2018-05-09 12:47:26.983095",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "exemption_category",
+  "max_amount",
+  "is_active"
+ ],
+ "fields": [
+  {
+   "fieldname": "exemption_category",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Tax Exemption Category",
+   "options": "Employee Tax Exemption Category",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "exemption_category.max_amount",
+   "fetch_if_empty": 1,
+   "fieldname": "max_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Max Exemption Amount"
+  },
+  {
+   "default": "1",
+   "fieldname": "is_active",
+   "fieldtype": "Check",
+   "label": "Is Active"
+  }
+ ],
+ "links": [],
+ "modified": "2020-05-27 23:18:08.254645",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Employee Tax Exemption Sub Category",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py
new file mode 100644
index 0000000..a8dd7e4
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import flt
+from frappe.model.document import Document
+
+class EmployeeTaxExemptionSubCategory(Document):
+	def validate(self):
+		category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount")
+		if flt(self.max_amount) > flt(category_max_amount):
+			frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}")
+				.format(category_max_amount, self.exemption_category))
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
new file mode 100644
index 0000000..8a1a6d1
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Employee Tax Exemption Sub Category", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Employee Tax Exemption Sub Category
+		() => frappe.tests.make('Employee Tax Exemption Sub Category', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py
new file mode 100644
index 0000000..5d705567
--- /dev/null
+++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestEmployeeTaxExemptionSubCategory(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/income_tax_slab/__init__.py b/erpnext/payroll/doctype/income_tax_slab/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab/__init__.py
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js
new file mode 100644
index 0000000..73a54eb
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js
@@ -0,0 +1,6 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Income Tax Slab', {
+
+});
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json
new file mode 100644
index 0000000..72b4332
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json
@@ -0,0 +1,152 @@
+{
+ "actions": [],
+ "autoname": "Prompt",
+ "creation": "2020-03-17 16:50:35.564915",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "effective_from",
+  "company",
+  "column_break_3",
+  "allow_tax_exemption",
+  "standard_tax_exemption_amount",
+  "disabled",
+  "amended_from",
+  "taxable_salary_slabs_section",
+  "slabs",
+  "taxes_and_charges_on_income_tax_section",
+  "other_taxes_and_charges"
+ ],
+ "fields": [
+  {
+   "fieldname": "effective_from",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Effective from",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "If enabled, Tax Exemption Declaration will be considered for income tax calculation.",
+   "fieldname": "allow_tax_exemption",
+   "fieldtype": "Check",
+   "label": "Allow Tax Exemption"
+  },
+  {
+   "fieldname": "taxable_salary_slabs_section",
+   "fieldtype": "Section Break",
+   "label": "Taxable Salary Slabs"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Income Tax Slab",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "slabs",
+   "fieldtype": "Table",
+   "label": "Taxable Salary Slabs",
+   "options": "Taxable Salary Slab",
+   "reqd": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "label": "Disabled"
+  },
+  {
+   "depends_on": "allow_tax_exemption",
+   "fieldname": "standard_tax_exemption_amount",
+   "fieldtype": "Currency",
+   "label": "Standard Tax Exemption Amount",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "collapsible": 1,
+   "collapsible_depends_on": "other_taxes_and_charges",
+   "fieldname": "taxes_and_charges_on_income_tax_section",
+   "fieldtype": "Section Break",
+   "label": "Taxes and Charges on Income Tax"
+  },
+  {
+   "fieldname": "other_taxes_and_charges",
+   "fieldtype": "Table",
+   "label": "Other Taxes and Charges",
+   "options": "Income Tax Slab Other Charges"
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 20:27:13.425084",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Income Tax Slab",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
new file mode 100644
index 0000000..253f023
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class IncomeTaxSlab(Document):
+	pass
diff --git a/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py
new file mode 100644
index 0000000..deaaf65
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestIncomeTaxSlab(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py
diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json
new file mode 100644
index 0000000..2531c79
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json
@@ -0,0 +1,75 @@
+{
+ "actions": [],
+ "creation": "2020-04-24 11:46:59.041180",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "description",
+  "column_break_2",
+  "percent",
+  "conditions_section",
+  "min_taxable_income",
+  "column_break_7",
+  "max_taxable_income"
+ ],
+ "fields": [
+  {
+   "columns": 4,
+   "fieldname": "description",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Description",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "percent",
+   "fieldtype": "Percent",
+   "in_list_view": 1,
+   "label": "Percent",
+   "reqd": 1
+  },
+  {
+   "fieldname": "conditions_section",
+   "fieldtype": "Section Break",
+   "label": "Conditions"
+  },
+  {
+   "columns": 2,
+   "fieldname": "min_taxable_income",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Min Taxable Income",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  },
+  {
+   "columns": 2,
+   "fieldname": "max_taxable_income",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Max Taxable Income",
+   "options": "Company:company:default_currency"
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:33:17.931912",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Income Tax Slab Other Charges",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py
new file mode 100644
index 0000000..b4098ec
--- /dev/null
+++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class IncomeTaxSlabOtherCharges(Document):
+	pass
diff --git a/erpnext/payroll/doctype/payroll_employee_detail/__init__.py b/erpnext/payroll/doctype/payroll_employee_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_employee_detail/__init__.py
diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json
new file mode 100644
index 0000000..b22e5de
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json
@@ -0,0 +1,66 @@
+{
+ "actions": [],
+ "creation": "2017-11-30 06:07:33.477781",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "column_break_3",
+  "department",
+  "designation"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Employee Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.designation",
+   "fieldname": "designation",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Designation",
+   "read_only": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:25:13.779032",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll Employee Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
new file mode 100644
index 0000000..aeb11fd
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+class PayrollEmployeeDetail(Document):
+	pass
diff --git a/erpnext/payroll/doctype/payroll_entry/__init__.py b/erpnext/payroll/doctype/payroll_entry/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/__init__.py
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
new file mode 100644
index 0000000..1ae3553
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js
@@ -0,0 +1,277 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+var in_progress = false;
+
+frappe.ui.form.on('Payroll Entry', {
+	onload: function (frm) {
+		if (!frm.doc.posting_date) {
+			frm.doc.posting_date = frappe.datetime.nowdate();
+		}
+		frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
+
+		frm.set_query("department", function() {
+			return {
+				"filters": {
+					"company": frm.doc.company,
+				}
+			};
+		});
+	},
+
+	refresh: function(frm) {
+		if (frm.doc.docstatus == 0) {
+			if(!frm.is_new()) {
+				frm.page.clear_primary_action();
+				frm.add_custom_button(__("Get Employees"),
+					function() {
+						frm.events.get_employee_details(frm);
+					}
+				).toggleClass('btn-primary', !(frm.doc.employees || []).length);
+			}
+			if ((frm.doc.employees || []).length) {
+				frm.page.set_primary_action(__('Create Salary Slips'), () => {
+					frm.save('Submit').then(()=>{
+						frm.page.clear_primary_action();
+						frm.refresh();
+						frm.events.refresh(frm);
+					});
+				});
+			}
+		}
+		if (frm.doc.docstatus == 1) {
+			if (frm.custom_buttons) frm.clear_custom_buttons();
+			frm.events.add_context_buttons(frm);
+		}
+	},
+
+	get_employee_details: function (frm) {
+		return frappe.call({
+			doc: frm.doc,
+			method: 'fill_employee_details',
+			callback: function(r) {
+				if (r.docs[0].employees){
+					frm.save();
+					frm.refresh();
+					if(r.docs[0].validate_attendance){
+						render_employee_attendance(frm, r.message);
+					}
+				}
+			}
+		})
+	},
+
+	create_salary_slips: function(frm) {
+		frm.call({
+			doc: frm.doc,
+			method: "create_salary_slips",
+			callback: function(r) {
+				frm.refresh();
+				frm.toolbar.refresh();
+			}
+		})
+	},
+
+	add_context_buttons: function(frm) {
+		if(frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
+			frm.events.add_bank_entry_button(frm);
+		} else if(frm.doc.salary_slips_created) {
+			frm.add_custom_button(__("Submit Salary Slip"), function() {
+				submit_salary_slip(frm);
+			}).addClass("btn-primary");
+		}
+	},
+
+	add_bank_entry_button: function(frm) {
+		frappe.call({
+			method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries',
+			args: {
+				'name': frm.doc.name
+			},
+			callback: function(r) {
+				if (r.message && !r.message.submitted) {
+					frm.add_custom_button("Make Bank Entry", function() {
+						make_bank_entry(frm);
+					}).addClass("btn-primary");
+				}
+			}
+		});
+	},
+
+	setup: function (frm) {
+		frm.add_fetch('company', 'cost_center', 'cost_center');
+
+		frm.set_query("payment_account", function () {
+			var account_types = ["Bank", "Cash"];
+			return {
+				filters: {
+					"account_type": ["in", account_types],
+					"is_group": 0,
+					"company": frm.doc.company
+				}
+			};
+		}),
+		frm.set_query("cost_center", function () {
+			return {
+				filters: {
+					"is_group": 0,
+					company: frm.doc.company
+				}
+			};
+		}),
+		frm.set_query("project", function () {
+			return {
+				filters: {
+					company: frm.doc.company
+				}
+			};
+		});
+	},
+
+	payroll_frequency: function (frm) {
+		frm.trigger("set_start_end_dates");
+		frm.events.clear_employee_table(frm);
+	},
+
+	company: function (frm) {
+		frm.events.clear_employee_table(frm);
+	},
+
+	department: function (frm) {
+		frm.events.clear_employee_table(frm);
+	},
+
+	designation: function (frm) {
+		frm.events.clear_employee_table(frm);
+	},
+
+	branch: function (frm) {
+		frm.events.clear_employee_table(frm);
+	},
+
+	start_date: function (frm) {
+		if(!in_progress && frm.doc.start_date){
+			frm.trigger("set_end_date");
+		}else{
+			// reset flag
+			in_progress = false;
+		}
+		frm.events.clear_employee_table(frm);
+	},
+
+	project: function (frm) {
+		frm.events.clear_employee_table(frm);
+	},
+
+	salary_slip_based_on_timesheet: function (frm) {
+		frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
+	},
+
+	set_start_end_dates: function (frm) {
+		if (!frm.doc.salary_slip_based_on_timesheet) {
+			frappe.call({
+				method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_start_end_dates',
+				args: {
+					payroll_frequency: frm.doc.payroll_frequency,
+					start_date: frm.doc.posting_date
+				},
+				callback: function (r) {
+					if (r.message) {
+						in_progress = true;
+						frm.set_value('start_date', r.message.start_date);
+						frm.set_value('end_date', r.message.end_date);
+					}
+				}
+			});
+		}
+	},
+
+	set_end_date: function(frm){
+		frappe.call({
+			method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date',
+			args: {
+				frequency: frm.doc.payroll_frequency,
+				start_date: frm.doc.start_date
+			},
+			callback: function (r) {
+				if (r.message) {
+					frm.set_value('end_date', r.message.end_date);
+				}
+			}
+		});
+	},
+
+	validate_attendance: function(frm){
+		if(frm.doc.validate_attendance && frm.doc.employees){
+			frappe.call({
+				method: 'validate_employee_attendance',
+				args: {},
+				callback: function(r) {
+					render_employee_attendance(frm, r.message);
+				},
+				doc: frm.doc,
+				freeze: true,
+				freeze_message: 'Validating Employee Attendance...'
+			});
+		}else{
+			frm.fields_dict.attendance_detail_html.html("");
+		}
+	},
+
+	clear_employee_table: function (frm) {
+		frm.clear_table('employees');
+		frm.refresh();
+	},
+});
+
+// Submit salary slips
+
+const submit_salary_slip = function (frm) {
+	frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'),
+		function() {
+			frappe.call({
+				method: 'submit_salary_slips',
+				args: {},
+				callback: function() {frm.events.refresh(frm);},
+				doc: frm.doc,
+				freeze: true,
+				freeze_message: 'Submitting Salary Slips and creating Journal Entry...'
+			});
+		},
+		function() {
+			if(frappe.dom.freeze_count) {
+				frappe.dom.unfreeze();
+				frm.events.refresh(frm);
+			}
+		}
+	);
+};
+
+let make_bank_entry = function (frm) {
+	var doc = frm.doc;
+	if (doc.payment_account) {
+		return frappe.call({
+			doc: cur_frm.doc,
+			method: "make_payment_entry",
+			callback: function() {
+				frappe.set_route(
+					'List', 'Journal Entry', {"Journal Entry Account.reference_name": frm.doc.name}
+				);
+			},
+			freeze: true,
+			freeze_message: __("Creating Payment Entries......")
+		});
+	} else {
+		frappe.msgprint(__("Payment Account is mandatory"));
+		frm.scroll_to_field('payment_account');
+	}
+};
+
+
+let render_employee_attendance = function(frm, data) {
+	frm.fields_dict.attendance_detail_html.html(
+		frappe.render_template('employees_to_mark_attendance', {
+			data: data
+		})
+	);
+}
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
new file mode 100644
index 0000000..4888be2
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json
@@ -0,0 +1,285 @@
+{
+ "actions": [],
+ "allow_copy": 1,
+ "autoname": "HR-PRUN-.YYYY.-.#####",
+ "creation": "2017-10-23 15:22:29.291323",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "engine": "InnoDB",
+ "field_order": [
+  "section_break0",
+  "column_break0",
+  "posting_date",
+  "payroll_frequency",
+  "column_break1",
+  "company",
+  "section_break_8",
+  "branch",
+  "department",
+  "column_break_10",
+  "designation",
+  "number_of_employees",
+  "sec_break20",
+  "employees",
+  "section_break_13",
+  "validate_attendance",
+  "attendance_detail_html",
+  "section_break_12",
+  "salary_slip_based_on_timesheet",
+  "select_payroll_period",
+  "start_date",
+  "end_date",
+  "column_break_11",
+  "deduct_tax_for_unclaimed_employee_benefits",
+  "deduct_tax_for_unsubmitted_tax_exemption_proof",
+  "accounting_dimensions_section",
+  "project",
+  "dimension_col_break",
+  "cost_center",
+  "account",
+  "payment_account",
+  "amended_from",
+  "column_break_33",
+  "bank_account",
+  "salary_slips_created",
+  "salary_slips_submitted"
+ ],
+ "fields": [
+  {
+   "fieldname": "section_break0",
+   "fieldtype": "Section Break",
+   "label": "Select Employees"
+  },
+  {
+   "fieldname": "column_break0",
+   "fieldtype": "Column Break",
+   "width": "50%"
+  },
+  {
+   "default": "Today",
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.salary_slip_based_on_timesheet == 0",
+   "fieldname": "payroll_frequency",
+   "fieldtype": "Select",
+   "label": "Payroll Frequency",
+   "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break1",
+   "fieldtype": "Column Break",
+   "width": "50%"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "remember_last_selected_value": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_8",
+   "fieldtype": "Section Break",
+   "label": "Employees"
+  },
+  {
+   "fieldname": "branch",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Branch",
+   "options": "Branch"
+  },
+  {
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "label": "Department",
+   "options": "Department"
+  },
+  {
+   "fieldname": "column_break_10",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "label": "Designation",
+   "options": "Designation"
+  },
+  {
+   "fieldname": "number_of_employees",
+   "fieldtype": "Int",
+   "label": "Number Of Employees",
+   "read_only": 1
+  },
+  {
+   "fieldname": "sec_break20",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "employees",
+   "fieldtype": "Table",
+   "label": "Employee Details",
+   "options": "Payroll Employee Detail",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_13",
+   "fieldtype": "Section Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "validate_attendance",
+   "fieldtype": "Check",
+   "label": "Validate Attendance"
+  },
+  {
+   "fieldname": "attendance_detail_html",
+   "fieldtype": "HTML"
+  },
+  {
+   "fieldname": "section_break_12",
+   "fieldtype": "Section Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "salary_slip_based_on_timesheet",
+   "fieldtype": "Check",
+   "label": "Salary Slip Based on Timesheet"
+  },
+  {
+   "fieldname": "select_payroll_period",
+   "fieldtype": "Section Break",
+   "label": "Select Payroll Period"
+  },
+  {
+   "fieldname": "start_date",
+   "fieldtype": "Date",
+   "label": "Start Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "label": "End Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_tax_for_unclaimed_employee_benefits",
+   "fieldtype": "Check",
+   "label": "Deduct Tax For Unclaimed Employee Benefits"
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof",
+   "fieldtype": "Check",
+   "label": "Deduct Tax For Unsubmitted Tax Exemption Proof"
+  },
+  {
+   "default": ":Company",
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center",
+   "reqd": 1
+  },
+  {
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "label": "Project",
+   "options": "Project"
+  },
+  {
+   "fieldname": "account",
+   "fieldtype": "Section Break",
+   "label": "Payment Entry"
+  },
+  {
+   "allow_on_submit": 1,
+   "description": "Select Payment Account to make Bank Entry",
+   "fetch_from": "bank_account.account",
+   "fieldname": "payment_account",
+   "fieldtype": "Link",
+   "label": "Payment Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Payroll Entry",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "salary_slips_created",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Salary Slips Created",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "salary_slips_submitted",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Salary Slips Submitted",
+   "read_only": 1
+  },
+  {
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "bank_account",
+   "fieldtype": "Link",
+   "label": "Bank Account",
+   "options": "Bank Account"
+  },
+  {
+   "fieldname": "column_break_33",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "icon": "fa fa-cog",
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 20:06:06.953904",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll Entry",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
new file mode 100644
index 0000000..e6bb708
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -0,0 +1,553 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from dateutil.relativedelta import relativedelta
+from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT, date_diff
+from frappe import _
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+
+class PayrollEntry(Document):
+	def onload(self):
+		if not self.docstatus==1 or self.salary_slips_submitted:
+    			return
+
+		# check if salary slips were manually submitted
+		entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
+		if cint(entries) == len(self.employees):
+    			self.set_onload("submitted_ss", True)
+
+	def on_submit(self):
+		self.create_salary_slips()
+
+	def before_submit(self):
+		if self.validate_attendance:
+			if self.validate_employee_attendance():
+				frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
+
+	def on_cancel(self):
+		frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
+			where payroll_entry=%s """, (self.name)))
+
+	def get_emp_list(self):
+		"""
+			Returns list of active employees based on selected criteria
+			and for which salary structure exists
+		"""
+		cond = self.get_filter_condition()
+		cond += self.get_joining_relieving_condition()
+
+		condition = ''
+		if self.payroll_frequency:
+			condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
+
+		sal_struct = frappe.db.sql_list("""
+				select
+					name from `tabSalary Structure`
+				where
+					docstatus = 1 and
+					is_active = 'Yes'
+					and company = %(company)s and
+					ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
+					{condition}""".format(condition=condition),
+				{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
+
+		if sal_struct:
+			cond += "and t2.salary_structure IN %(sal_struct)s "
+			cond += "and %(from_date)s >= t2.from_date"
+			emp_list = frappe.db.sql("""
+				select
+					distinct t1.name as employee, t1.employee_name, t1.department, t1.designation
+				from
+					`tabEmployee` t1, `tabSalary Structure Assignment` t2
+				where
+					t1.name = t2.employee
+					and t2.docstatus = 1
+			%s order by t2.from_date desc
+			""" % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date}, as_dict=True)
+			return emp_list
+
+	def fill_employee_details(self):
+		self.set('employees', [])
+		employees = self.get_emp_list()
+		if not employees:
+			frappe.throw(_("No employees for the mentioned criteria"))
+
+		for d in employees:
+			self.append('employees', d)
+
+		self.number_of_employees = len(employees)
+		if self.validate_attendance:
+			return self.validate_employee_attendance()
+
+	def get_filter_condition(self):
+		self.check_mandatory()
+
+		cond = ''
+		for f in ['company', 'branch', 'department', 'designation']:
+			if self.get(f):
+				cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'"
+
+		return cond
+
+	def get_joining_relieving_condition(self):
+		cond = """
+			and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
+			and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s'
+		""" % {"start_date": self.start_date, "end_date": self.end_date}
+		return cond
+
+	def check_mandatory(self):
+		for fieldname in ['company', 'start_date', 'end_date']:
+			if not self.get(fieldname):
+				frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))
+
+	def create_salary_slips(self):
+		"""
+			Creates salary slip for selected employees if already not created
+		"""
+		self.check_permission('write')
+		self.created = 1
+		emp_list = [d.employee for d in self.get_emp_list()]
+		if emp_list:
+			args = frappe._dict({
+				"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
+				"payroll_frequency": self.payroll_frequency,
+				"start_date": self.start_date,
+				"end_date": self.end_date,
+				"company": self.company,
+				"posting_date": self.posting_date,
+				"deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits,
+				"deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof,
+				"payroll_entry": self.name
+			})
+			if len(emp_list) > 30:
+				frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=emp_list, args=args)
+			else:
+				create_salary_slips_for_employees(emp_list, args, publish_progress=False)
+				# since this method is called via frm.call this doc needs to be updated manually
+				self.reload()
+
+	def get_sal_slip_list(self, ss_status, as_dict=False):
+		"""
+			Returns list of salary slips based on selected criteria
+		"""
+		cond = self.get_filter_condition()
+
+		ss_list = frappe.db.sql("""
+			select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
+			where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
+			and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
+		""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
+		return ss_list
+
+	def submit_salary_slips(self):
+		self.check_permission('write')
+		ss_list = self.get_sal_slip_list(ss_status=0)
+		if len(ss_list) > 30:
+			frappe.enqueue(submit_salary_slips_for_employees, timeout=600, payroll_entry=self, salary_slips=ss_list)
+		else:
+			submit_salary_slips_for_employees(self, ss_list, publish_progress=False)
+
+	def email_salary_slip(self, submitted_ss):
+		if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"):
+			for ss in submitted_ss:
+				ss.email_salary_slip()
+
+	def get_salary_component_account(self, salary_component):
+		account = frappe.db.get_value("Salary Component Account",
+			{"parent": salary_component, "company": self.company}, "default_account")
+
+		if not account:
+			frappe.throw(_("Please set default account in Salary Component {0}")
+				.format(salary_component))
+
+		return account
+
+	def get_salary_components(self, component_type):
+		salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
+		if salary_slips:
+			salary_components = frappe.db.sql("""
+				select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center
+				from `tabSalary Slip` ss, `tabSalary Detail` ssd
+				where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s)
+			""" % (component_type, ', '.join(['%s']*len(salary_slips))),
+				tuple([d.name for d in salary_slips]), as_dict=True)
+
+			return salary_components
+
+	def get_salary_component_total(self, component_type = None):
+		salary_components = self.get_salary_components(component_type)
+		if salary_components:
+			component_dict = {}
+			for item in salary_components:
+				add_component_to_accrual_jv_entry = True
+				if component_type == "earnings":
+					is_flexible_benefit, only_tax_impact = frappe.db.get_value("Salary Component", item['salary_component'], ['is_flexible_benefit', 'only_tax_impact'])
+					if is_flexible_benefit == 1 and only_tax_impact ==1:
+						add_component_to_accrual_jv_entry = False
+				if add_component_to_accrual_jv_entry:
+					component_dict[(item.salary_component, item.payroll_cost_center)] \
+						= component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount)
+			account_details = self.get_account(component_dict = component_dict)
+			return account_details
+
+	def get_account(self, component_dict = None):
+		account_dict = {}
+		for key, amount in component_dict.items():
+			account = self.get_salary_component_account(key[0])
+			account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount
+		return account_dict
+
+	def get_default_payroll_payable_account(self):
+		payroll_payable_account = frappe.get_cached_value('Company',
+			{"company_name": self.company},  "default_payroll_payable_account")
+
+		if not payroll_payable_account:
+			frappe.throw(_("Please set Default Payroll Payable Account in Company {0}")
+				.format(self.company))
+
+		return payroll_payable_account
+
+	def make_accrual_jv_entry(self):
+		self.check_permission('write')
+		earnings = self.get_salary_component_total(component_type = "earnings") or {}
+		deductions = self.get_salary_component_total(component_type = "deductions") or {}
+		default_payroll_payable_account = self.get_default_payroll_payable_account()
+		jv_name = ""
+		precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
+
+		if earnings or deductions:
+			journal_entry = frappe.new_doc('Journal Entry')
+			journal_entry.voucher_type = 'Journal Entry'
+			journal_entry.user_remark = _('Accrual Journal Entry for salaries from {0} to {1}')\
+				.format(self.start_date, self.end_date)
+			journal_entry.company = self.company
+			journal_entry.posting_date = self.posting_date
+
+			accounts = []
+			payable_amount = 0
+
+			# Earnings
+			for acc_cc, amount in earnings.items():
+				payable_amount += flt(amount, precision)
+				accounts.append({
+						"account": acc_cc[0],
+						"debit_in_account_currency": flt(amount, precision),
+						"party_type": '',
+						"cost_center": acc_cc[1] or self.cost_center,
+						"project": self.project
+					})
+
+			# Deductions
+			for acc_cc, amount in deductions.items():
+				payable_amount -= flt(amount, precision)
+				accounts.append({
+						"account": acc_cc[0],
+						"credit_in_account_currency": flt(amount, precision),
+						"cost_center": acc_cc[1] or self.cost_center,
+						"party_type": '',
+						"project": self.project
+					})
+
+			# Payable amount
+			accounts.append({
+				"account": default_payroll_payable_account,
+				"credit_in_account_currency": flt(payable_amount, precision),
+				"party_type": '',
+				"cost_center": self.cost_center
+			})
+
+			journal_entry.set("accounts", accounts)
+			journal_entry.title = default_payroll_payable_account
+			journal_entry.save()
+
+			try:
+				journal_entry.submit()
+				jv_name = journal_entry.name
+				self.update_salary_slip_status(jv_name = jv_name)
+			except Exception as e:
+				frappe.msgprint(e)
+
+		return jv_name
+
+	def make_payment_entry(self):
+		self.check_permission('write')
+
+		cond = self.get_filter_condition()
+		salary_slip_name_list = frappe.db.sql(""" select t1.name from `tabSalary Slip` t1
+			where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
+			""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_list = True)
+
+		if salary_slip_name_list and len(salary_slip_name_list) > 0:
+			salary_slip_total = 0
+			for salary_slip_name in salary_slip_name_list:
+				salary_slip = frappe.get_doc("Salary Slip", salary_slip_name[0])
+				for sal_detail in salary_slip.earnings:
+					is_flexible_benefit, only_tax_impact, creat_separate_je, statistical_component = frappe.db.get_value("Salary Component", sal_detail.salary_component,
+						['is_flexible_benefit', 'only_tax_impact', 'create_separate_payment_entry_against_benefit_claim', 'statistical_component'])
+					if only_tax_impact != 1 and statistical_component != 1:
+						if is_flexible_benefit == 1 and creat_separate_je == 1:
+							self.create_journal_entry(sal_detail.amount, sal_detail.salary_component)
+						else:
+							salary_slip_total += sal_detail.amount
+				for sal_detail in salary_slip.deductions:
+					statistical_component = frappe.db.get_value("Salary Component", sal_detail.salary_component, 'statistical_component')
+					if statistical_component != 1:
+						salary_slip_total -= sal_detail.amount
+			if salary_slip_total > 0:
+				self.create_journal_entry(salary_slip_total, "salary")
+
+	def create_journal_entry(self, je_payment_amount, user_remark):
+		default_payroll_payable_account = self.get_default_payroll_payable_account()
+		precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
+
+		journal_entry = frappe.new_doc('Journal Entry')
+		journal_entry.voucher_type = 'Bank Entry'
+		journal_entry.user_remark = _('Payment of {0} from {1} to {2}')\
+			.format(user_remark, self.start_date, self.end_date)
+		journal_entry.company = self.company
+		journal_entry.posting_date = self.posting_date
+
+		payment_amount = flt(je_payment_amount, precision)
+
+		journal_entry.set("accounts", [
+			{
+				"account": self.payment_account,
+				"bank_account": self.bank_account,
+				"credit_in_account_currency": payment_amount
+			},
+			{
+				"account": default_payroll_payable_account,
+				"debit_in_account_currency": payment_amount,
+				"reference_type": self.doctype,
+				"reference_name": self.name
+			}
+		])
+		journal_entry.save(ignore_permissions = True)
+
+	def update_salary_slip_status(self, jv_name = None):
+		ss_list = self.get_sal_slip_list(ss_status=1)
+		for ss in ss_list:
+			ss_obj = frappe.get_doc("Salary Slip",ss[0])
+			frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)
+
+	def set_start_end_dates(self):
+		self.update(get_start_end_dates(self.payroll_frequency,
+			self.start_date or self.posting_date, self.company))
+
+	def validate_employee_attendance(self):
+		employees_to_mark_attendance = []
+		days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
+		for employee_detail in self.employees:
+			days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
+			days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
+			days_in_payroll = date_diff(self.end_date, self.start_date) + 1
+			if days_in_payroll > days_holiday + days_attendance_marked:
+				employees_to_mark_attendance.append({
+					"employee": employee_detail.employee,
+					"employee_name": employee_detail.employee_name
+					})
+		return employees_to_mark_attendance
+
+	def get_count_holidays_of_employee(self, employee):
+		holiday_list = get_holiday_list_for_employee(employee)
+		holidays = 0
+		if holiday_list:
+			days = frappe.db.sql("""select count(*) from tabHoliday where
+				parent=%s and holiday_date between %s and %s""", (holiday_list,
+				self.start_date, self.end_date))
+			if days and days[0][0]:
+				holidays = days[0][0]
+		return holidays
+
+	def get_count_employee_attendance(self, employee):
+		marked_days = 0
+		attendances = frappe.db.sql("""select count(*) from tabAttendance where
+			employee=%s and docstatus=1 and attendance_date between %s and %s""",
+			(employee, self.start_date, self.end_date))
+		if attendances and attendances[0][0]:
+			marked_days = attendances[0][0]
+		return marked_days
+
+@frappe.whitelist()
+def get_start_end_dates(payroll_frequency, start_date=None, company=None):
+	'''Returns dict of start and end dates for given payroll frequency based on start_date'''
+
+	if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly" or payroll_frequency == "":
+		fiscal_year = get_fiscal_year(start_date, company=company)[0]
+		month = "%02d" % getdate(start_date).month
+		m = get_month_details(fiscal_year, month)
+		if payroll_frequency == "Bimonthly":
+			if getdate(start_date).day <= 15:
+				start_date = m['month_start_date']
+				end_date = m['month_mid_end_date']
+			else:
+				start_date = m['month_mid_start_date']
+				end_date = m['month_end_date']
+		else:
+			start_date = m['month_start_date']
+			end_date = m['month_end_date']
+
+	if payroll_frequency == "Weekly":
+		end_date = add_days(start_date, 6)
+
+	if payroll_frequency == "Fortnightly":
+		end_date = add_days(start_date, 13)
+
+	if payroll_frequency == "Daily":
+		end_date = start_date
+
+	return frappe._dict({
+		'start_date': start_date, 'end_date': end_date
+	})
+
+def get_frequency_kwargs(frequency_name):
+	frequency_dict = {
+		'monthly': {'months': 1},
+		'fortnightly': {'days': 14},
+		'weekly': {'days': 7},
+		'daily': {'days': 1}
+	}
+	return frequency_dict.get(frequency_name)
+
+
+@frappe.whitelist()
+def get_end_date(start_date, frequency):
+	start_date = getdate(start_date)
+	frequency = frequency.lower() if frequency else 'monthly'
+	kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')
+
+	# weekly, fortnightly and daily intervals have fixed days so no problems
+	end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
+	if frequency != 'bimonthly':
+		return dict(end_date=end_date.strftime(DATE_FORMAT))
+
+	else:
+		return dict(end_date='')
+
+
+def get_month_details(year, month):
+	ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
+	if ysd:
+		import calendar, datetime
+		diff_mnt = cint(month)-cint(ysd.month)
+		if diff_mnt<0:
+			diff_mnt = 12-int(ysd.month)+cint(month)
+		msd = ysd + relativedelta(months=diff_mnt) # month start date
+		month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
+		mid_start = datetime.date(msd.year, cint(month), 16) # month mid start date
+		mid_end = datetime.date(msd.year, cint(month), 15) # month mid end date
+		med = datetime.date(msd.year, cint(month), month_days) # month end date
+		return frappe._dict({
+			'year': msd.year,
+			'month_start_date': msd,
+			'month_end_date': med,
+			'month_mid_start_date': mid_start,
+			'month_mid_end_date': mid_end,
+			'month_days': month_days
+		})
+	else:
+		frappe.throw(_("Fiscal Year {0} not found").format(year))
+
+def get_payroll_entry_bank_entries(payroll_entry_name):
+	journal_entries = frappe.db.sql(
+		'select name from `tabJournal Entry Account` '
+		'where reference_type="Payroll Entry" '
+		'and reference_name=%s and docstatus=1',
+		payroll_entry_name,
+		as_dict=1
+	)
+
+	return journal_entries
+
+
+@frappe.whitelist()
+def payroll_entry_has_bank_entries(name):
+	response = {}
+	bank_entries = get_payroll_entry_bank_entries(name)
+	response['submitted'] = 1 if bank_entries else 0
+
+	return response
+
+def create_salary_slips_for_employees(employees, args, publish_progress=True):
+	salary_slips_exists_for = get_existing_salary_slips(employees, args)
+	count=0
+	for emp in employees:
+		if emp not in salary_slips_exists_for:
+			args.update({
+				"doctype": "Salary Slip",
+				"employee": emp
+			})
+			ss = frappe.get_doc(args)
+			ss.insert()
+			count+=1
+			if publish_progress:
+				frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)),
+					title = _("Creating Salary Slips..."))
+
+	payroll_entry = frappe.get_doc("Payroll Entry", args.payroll_entry)
+	payroll_entry.db_set("salary_slips_created", 1)
+	payroll_entry.notify_update()
+
+def get_existing_salary_slips(employees, args):
+	return frappe.db.sql_list("""
+		select distinct employee from `tabSalary Slip`
+		where docstatus!= 2 and company = %s
+			and start_date >= %s and end_date <= %s
+			and employee in (%s)
+	""" % ('%s', '%s', '%s', ', '.join(['%s']*len(employees))),
+		[args.company, args.start_date, args.end_date] + employees)
+
+def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progress=True):
+	submitted_ss = []
+	not_submitted_ss = []
+	frappe.flags.via_payroll_entry = True
+
+	count = 0
+	for ss in salary_slips:
+		ss_obj = frappe.get_doc("Salary Slip",ss[0])
+		if ss_obj.net_pay<0:
+			not_submitted_ss.append(ss[0])
+		else:
+			try:
+				ss_obj.submit()
+				submitted_ss.append(ss_obj)
+			except frappe.ValidationError:
+				not_submitted_ss.append(ss[0])
+
+		count += 1
+		if publish_progress:
+			frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
+	if submitted_ss:
+		payroll_entry.make_accrual_jv_entry()
+		frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
+			.format(ss_obj.start_date, ss_obj.end_date))
+
+		payroll_entry.email_salary_slip(submitted_ss)
+
+		payroll_entry.db_set("salary_slips_submitted", 1)
+		payroll_entry.notify_update()
+
+	if not submitted_ss and not not_submitted_ss:
+		frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
+
+	if not_submitted_ss:
+		frappe.msgprint(_("Could not submit some Salary Slips"))
+
+def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
+	return frappe.db.sql("""
+		select name from `tabPayroll Entry`
+		where `{key}` LIKE %(txt)s
+		and name not in
+			(select reference_name from `tabJournal Entry Account`
+				where reference_type="Payroll Entry")
+		order by name limit %(start)s, %(page_len)s"""
+		.format(key=searchfield), {
+			'txt': "%%%s%%" % frappe.db.escape(txt),
+			'start': start, 'page_len': page_len
+		})
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
new file mode 100644
index 0000000..7af507d
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py
@@ -0,0 +1,16 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'payroll_entry',
+		'non_standard_fieldnames': {
+			'Journal Entry': 'reference_name',
+			'Payment Entry': 'reference_name',
+		},
+		'transactions': [
+			{
+				'items': ['Salary Slip', 'Journal Entry']
+			}
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js
new file mode 100644
index 0000000..d24f243
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js
@@ -0,0 +1,62 @@
+QUnit.module('HR');
+
+QUnit.test("test: Payroll Entry", function (assert) {
+	assert.expect(5);
+	let done = assert.async();
+	let employees, docname;
+
+	frappe.run_serially([
+		() => {
+			return frappe.tests.make('Payroll Entry', [
+				{company: 'For Testing'},
+				{posting_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
+				{payroll_frequency: 'Monthly'},
+				{cost_center: 'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
+			]);
+		},
+
+		() => frappe.timeout(1),
+		() => {
+			assert.equal(cur_frm.doc.company, 'For Testing');
+			assert.equal(cur_frm.doc.posting_date, frappe.datetime.add_days(frappe.datetime.nowdate(), 0));
+			assert.equal(cur_frm.doc.cost_center, 'Main - FT');
+		},
+		() => frappe.click_button('Get Employee Details'),
+		() => {
+			employees = cur_frm.doc.employees.length;
+			docname = cur_frm.doc.name;
+		},
+
+		() => frappe.click_button('Submit'),
+		() => frappe.timeout(1),
+		() => frappe.click_button('Yes'),
+		() => frappe.timeout(5),
+
+		() => frappe.click_button('View Salary Slip'),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_list.data.length, employees),
+
+		() => frappe.set_route('Form', 'Payroll Entry', docname),
+		() => frappe.timeout(2),
+		() => frappe.click_button('Submit Salary Slip'),
+		() => frappe.click_button('Yes'),
+		() => frappe.timeout(5),
+
+		() => frappe.click_button('Close'),
+		() => frappe.timeout(1),
+
+		() => frappe.click_button('View Salary Slip'),
+		() => frappe.timeout(2),
+		() => {
+			let count = 0;
+			for(var i = 0; i < employees; i++) {
+				if(cur_list.data[i].docstatus == 1){
+					count++;
+				}
+			}
+			assert.equal(count, employees, "Salary Slip submitted for all employees");
+		},
+
+		() => done()
+	]);
+});
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
new file mode 100644
index 0000000..b0f225d
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -0,0 +1,214 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+from __future__ import unicode_literals
+import unittest
+import erpnext
+import frappe
+from dateutil.relativedelta import relativedelta
+from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
+from frappe.utils import add_months
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
+		make_earning_salary_component, make_deduction_salary_component, create_account
+from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
+from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
+
+class TestPayrollEntry(unittest.TestCase):
+	def setUp(self):
+		for dt in ["Salary Slip", "Salary Component", "Salary Component Account",
+			"Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]:
+				frappe.db.sql("delete from `tab%s`" % dt)
+
+		make_earning_salary_component(setup=True, company_list=["_Test Company"])
+		make_deduction_salary_component(setup=True, company_list=["_Test Company"])
+
+		frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
+
+	def test_payroll_entry(self): # pylint: disable=no-self-use
+		company = erpnext.get_default_company()
+		for data in frappe.get_all('Salary Component', fields = ["name"]):
+			if not frappe.db.get_value('Salary Component Account',
+				{'parent': data.name, 'company': company}, 'name'):
+				get_salary_component_account(data.name)
+
+		employee = frappe.db.get_value("Employee", {'company': company})
+		make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company)
+		dates = get_start_end_dates('Monthly', nowdate())
+		if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
+			make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date)
+
+	def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use
+		for data in frappe.get_all('Salary Component', fields = ["name"]):
+			if not frappe.db.get_value('Salary Component Account',
+				{'parent': data.name, 'company': "_Test Company"}, 'name'):
+				get_salary_component_account(data.name)
+
+		if not frappe.db.exists('Department', "cc - _TC"):
+			frappe.get_doc({
+				'doctype': 'Department',
+				'department_name': "cc",
+				"company": "_Test Company"
+			}).insert()
+
+		employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
+			department="cc - _TC", company="_Test Company")
+		employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC",
+			department="cc - _TC", company="_Test Company")
+
+		make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company")
+		make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company")
+
+		if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
+			create_account(account_name="_Test Payroll Payable",
+				company="_Test Company", parent_account="Current Liabilities - _TC")
+			frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
+				"_Test Payroll Payable - _TC")
+
+		dates = get_start_end_dates('Monthly', nowdate())
+		if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
+			pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
+				department="cc - _TC", company="_Test Company", payment_account="Cash - _TC", cost_center="Main - _TC")
+			je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry")
+			je_entries = frappe.db.sql("""
+				select account, cost_center, debit, credit
+				from `tabJournal Entry Account`
+				where parent=%s
+				order by account, cost_center
+			""", je)
+			expected_je = (
+				('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
+				('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0),
+				('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0),
+				('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0),
+				('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0)
+			)
+
+			self.assertEqual(je_entries, expected_je)
+
+	def test_get_end_date(self):
+		self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
+		self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
+		self.assertEqual(get_end_date('2017-02-01', 'fortnightly'), {'end_date': '2017-02-14'})
+		self.assertEqual(get_end_date('2017-02-01', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2017-01-01', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
+		self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
+
+	def test_loan(self):
+		branch = "Test Employee Branch"
+		applicant = make_employee("test_employee@loan.com", company="_Test Company")
+		company = "_Test Company"
+		holiday_list = make_holiday("test holiday for loan")
+
+		company_doc = frappe.get_doc('Company', company)
+		if not company_doc.default_payroll_payable_account:
+			company_doc.default_payroll_payable_account = frappe.db.get_value('Account',
+				{'company': company, 'root_type': 'Liability', 'account_type': ''}, 'name')
+			company_doc.save()
+
+		if not frappe.db.exists('Branch', branch):
+			frappe.get_doc({
+				'doctype': 'Branch',
+				'branch': branch
+			}).insert()
+
+		employee_doc = frappe.get_doc('Employee', applicant)
+		employee_doc.branch = branch
+		employee_doc.holiday_list = holiday_list
+		employee_doc.save()
+
+		salary_structure = "Test Salary Structure for Loan"
+		make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company")
+
+		loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
+		loan.repay_from_salary = 1
+		loan.submit()
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
+
+		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
+
+
+		dates = get_start_end_dates('Monthly', nowdate())
+		make_payroll_entry(company="_Test Company", start_date=dates.start_date,
+			end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
+
+		name = frappe.db.get_value('Salary Slip',
+			{'posting_date': nowdate(), 'employee': applicant}, 'name')
+
+		salary_slip = frappe.get_doc('Salary Slip', name)
+		for row in salary_slip.loans:
+			if row.loan == loan.name:
+				interest_amount = (280000 * 8.4)/(12*100)
+				principal_amount = loan.monthly_repayment_amount - interest_amount
+				self.assertEqual(row.interest_amount, interest_amount)
+				self.assertEqual(row.principal_amount, principal_amount)
+				self.assertEqual(row.total_payment,
+					interest_amount + principal_amount)
+
+		if salary_slip.docstatus == 0:
+			frappe.delete_doc('Salary Slip', name)
+
+
+def make_payroll_entry(**args):
+	args = frappe._dict(args)
+
+	payroll_entry = frappe.new_doc("Payroll Entry")
+	payroll_entry.company = args.company or erpnext.get_default_company()
+	payroll_entry.start_date = args.start_date or "2016-11-01"
+	payroll_entry.end_date = args.end_date or "2016-11-30"
+	payroll_entry.payment_account = get_payment_account()
+	payroll_entry.posting_date = nowdate()
+	payroll_entry.payroll_frequency = "Monthly"
+	payroll_entry.branch = args.branch or None
+	payroll_entry.department = args.department or None
+
+	if args.cost_center:
+		payroll_entry.cost_center = args.cost_center
+
+	if args.payment_account:
+		payroll_entry.payment_account = args.payment_account
+
+	payroll_entry.fill_employee_details()
+	payroll_entry.save()
+	payroll_entry.create_salary_slips()
+	payroll_entry.submit_salary_slips()
+	if payroll_entry.get_sal_slip_list(ss_status = 1):
+		payroll_entry.make_payment_entry()
+
+	return payroll_entry
+
+def get_payment_account():
+	return frappe.get_value('Account',
+		{'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+
+def make_holiday(holiday_list_name):
+	if not frappe.db.exists('Holiday List', holiday_list_name):
+		current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
+		dt = getdate(nowdate())
+
+		new_year = dt + relativedelta(month=1, day=1, year=dt.year)
+		republic_day = dt + relativedelta(month=1, day=26, year=dt.year)
+		test_holiday = dt + relativedelta(month=2, day=2, year=dt.year)
+
+		frappe.get_doc({
+			'doctype': 'Holiday List',
+			'from_date': current_fiscal_year.year_start_date,
+			'to_date': current_fiscal_year.year_end_date,
+			'holiday_list_name': holiday_list_name,
+			'holidays': [{
+				'holiday_date': new_year,
+				'description': 'New Year'
+			}, {
+				'holiday_date': republic_day,
+				'description': 'Republic Day'
+			}, {
+				'holiday_date': test_holiday,
+				'description': 'Test Holiday'
+			}]
+		}).insert()
+
+	return holiday_list_name
diff --git a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js
new file mode 100644
index 0000000..8ff5515
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js
@@ -0,0 +1,61 @@
+QUnit.module('HR');
+
+QUnit.test("test: Set Salary Components", function (assert) {
+	assert.expect(5);
+	let done = assert.async();
+
+	frappe.run_serially([
+		() => frappe.set_route('Form', 'Salary Component', 'Leave Encashment'),
+		() => {
+			var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
+			row.company = 'For Testing';
+			row.default_account = 'Salary - FT';
+		},
+
+		() => cur_frm.save(),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'),
+
+		() => frappe.set_route('Form', 'Salary Component', 'Basic'),
+		() => {
+			var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
+			row.company = 'For Testing';
+			row.default_account = 'Salary - FT';
+		},
+
+		() => cur_frm.save(),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'),
+
+		() => frappe.set_route('Form', 'Salary Component', 'Income Tax'),
+		() => {
+			var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
+			row.company = 'For Testing';
+			row.default_account = 'Salary - FT';
+		},
+
+		() => cur_frm.save(),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'),
+
+		() => frappe.set_route('Form', 'Salary Component', 'Arrear'),
+		() => {
+			var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts");
+			row.company = 'For Testing';
+			row.default_account = 'Salary - FT';
+		},
+
+		() => cur_frm.save(),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.accounts[0].default_account, 'Salary - FT'),
+
+		() => frappe.set_route('Form', 'Company', 'For Testing'),
+		() => cur_frm.set_value('default_payroll_payable_account', 'Payroll Payable - FT'),
+		() => cur_frm.save(),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.default_payroll_payable_account, 'Payroll Payable - FT'),
+
+		() => done()
+
+	]);
+});
diff --git a/erpnext/payroll/doctype/payroll_period/__init__.py b/erpnext/payroll/doctype/payroll_period/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/__init__.py
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.js b/erpnext/payroll/doctype/payroll_period/payroll_period.js
new file mode 100644
index 0000000..67caf9d
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payroll Period', {
+	refresh: function(frm) {
+
+	}
+});
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json
new file mode 100644
index 0000000..345a241
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.json
@@ -0,0 +1,102 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "Prompt",
+ "creation": "2018-04-13 15:18:53.698553",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "column_break_2",
+  "start_date",
+  "end_date",
+  "section_break_5",
+  "periods"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "start_date",
+   "fieldtype": "Date",
+   "label": "Start Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "label": "End Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break",
+   "hidden": 1,
+   "label": "Payroll Periods"
+  },
+  {
+   "fieldname": "periods",
+   "fieldtype": "Table",
+   "label": "Payroll Periods",
+   "options": "Payroll Period Date"
+  }
+ ],
+ "links": [],
+ "modified": "2020-05-27 20:12:32.684189",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll Period",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py
new file mode 100644
index 0000000..d7893d0
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
+from frappe.model.document import Document
+from erpnext.hr.utils import get_holidays_for_employee
+
+class PayrollPeriod(Document):
+	def validate(self):
+		self.validate_dates()
+		self.validate_overlap()
+
+	def validate_dates(self):
+		if getdate(self.start_date) > getdate(self.end_date):
+			frappe.throw(_("End date can not be less than start date"))
+
+	def validate_overlap(self):
+		query = """
+			select name
+			from `tab{0}`
+			where name != %(name)s
+			and company = %(company)s and (start_date between %(start_date)s and %(end_date)s \
+				or end_date between %(start_date)s and %(end_date)s \
+				or (start_date < %(start_date)s and end_date > %(end_date)s))
+			"""
+		if not self.name:
+			# hack! if name is null, it could cause problems with !=
+			self.name = "New "+self.doctype
+
+		overlap_doc = frappe.db.sql(query.format(self.doctype),{
+				"start_date": self.start_date,
+				"end_date": self.end_date,
+				"name": self.name,
+				"company": self.company
+			}, as_dict = 1)
+
+		if overlap_doc:
+			msg = _("A {0} exists between {1} and {2} (").format(self.doctype,
+				formatdate(self.start_date), formatdate(self.end_date)) \
+				+ """ <b><a href="#Form/{0}/{1}">{1}</a></b>""".format(self.doctype, overlap_doc[0].name) \
+				+ _(") for {0}").format(self.company)
+			frappe.throw(msg)
+
+def get_payroll_period_days(start_date, end_date, employee, company=None):
+	if not company:
+		company = frappe.db.get_value("Employee", employee, "company")
+	payroll_period = frappe.db.sql("""
+		select name, start_date, end_date
+		from `tabPayroll Period`
+		where
+			company=%(company)s
+			and %(start_date)s between start_date and end_date
+			and %(end_date)s between start_date and end_date
+	""", {
+		'company': company,
+		'start_date': start_date,
+		'end_date': end_date
+	})
+
+	if len(payroll_period) > 0:
+		actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1
+		working_days = actual_no_of_days
+		if not cint(frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days")):
+			holidays = get_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2]))
+			working_days -= len(holidays)
+		return payroll_period[0][0], working_days, actual_no_of_days
+	return False, False, False
+
+def get_payroll_period(from_date, to_date, company):
+	payroll_period = frappe.db.sql("""
+		select name, start_date, end_date
+		from `tabPayroll Period`
+		where start_date<=%s and end_date>= %s and company=%s
+	""", (from_date, to_date, company), as_dict=1)
+
+	return payroll_period[0] if payroll_period else None
+
+def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
+	# TODO if both deduct checked update the factor to make tax consistent
+	period_start, period_end = payroll_period.start_date, payroll_period.end_date
+	joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
+
+	if getdate(joining_date) > getdate(period_start):
+		period_start = joining_date
+	if relieving_date and getdate(relieving_date) < getdate(period_end):
+		period_end = relieving_date
+
+	total_sub_periods, remaining_sub_periods = 0.0, 0.0
+
+	if payroll_frequency ==  "Monthly" and not depends_on_payment_days:
+		total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
+		remaining_sub_periods = month_diff(period_end, start_date)
+	else:
+		salary_days = date_diff(end_date, start_date) + 1
+
+		days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1
+		total_sub_periods = flt(days_in_payroll_period) / flt(salary_days)
+
+		remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1
+		remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days)
+
+	return total_sub_periods, remaining_sub_periods
diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
new file mode 100644
index 0000000..4e9c7c9
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py
@@ -0,0 +1,12 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+     return {
+        'fieldname': 'payroll_period',
+        'transactions': [
+            {
+                'items': ['Employee Tax Exemption Proof Submission', 'Employee Tax Exemption Declaration']
+            },
+        ],
+    }
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.js b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
new file mode 100644
index 0000000..8c4ded9
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Payroll Period", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Payroll Period
+		() => frappe.tests.make('Payroll Period', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/payroll_period/test_payroll_period.py b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
new file mode 100644
index 0000000..d06dc73
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestPayrollPeriod(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/payroll_period_date/__init__.py b/erpnext/payroll/doctype/payroll_period_date/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period_date/__init__.py
diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json
new file mode 100644
index 0000000..d745fcd
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json
@@ -0,0 +1,39 @@
+{
+ "actions": [],
+ "creation": "2018-04-13 15:17:30.513630",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "start_date",
+  "end_date"
+ ],
+ "fields": [
+  {
+   "fieldname": "start_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Start Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "End Date",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:30:15.943356",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll Period Date",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
new file mode 100644
index 0000000..a3ee269
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class PayrollPeriodDate(Document):
+	pass
diff --git a/erpnext/payroll/doctype/payroll_settings/__init__.py b/erpnext/payroll/doctype/payroll_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_settings/__init__.py
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.js b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js
new file mode 100644
index 0000000..941464d
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js
@@ -0,0 +1,19 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payroll Settings', {
+	encrypt_salary_slips_in_emails: function(frm) {
+		let encrypt_state = frm.doc.encrypt_salary_slips_in_emails;
+		frm.set_df_property('password_policy', 'reqd', encrypt_state);
+	},
+
+	validate: function(frm) {
+		let policy = frm.doc.password_policy;
+		if (policy) {
+			if (policy.includes(' ') || policy.includes('--')) {
+				frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically"));
+			}
+			frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
+		}
+	},
+});
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
new file mode 100644
index 0000000..e3b8b3e
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
@@ -0,0 +1,130 @@
+{
+ "actions": [],
+ "creation": "2020-06-04 15:13:33.589685",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "payroll_based_on",
+  "consider_unmarked_attendance_as",
+  "max_working_hours_against_timesheet",
+  "include_holidays_in_total_working_days",
+  "disable_rounded_total",
+  "column_break_11",
+  "daily_wages_fraction_for_half_day",
+  "email_salary_slip_to_employee",
+  "encrypt_salary_slips_in_emails",
+  "password_policy"
+ ],
+ "fields": [
+  {
+   "default": "Leave",
+   "fieldname": "payroll_based_on",
+   "fieldtype": "Select",
+   "label": "Calculate Payroll Working Days Based On",
+   "options": "Leave\nAttendance",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "max_working_hours_against_timesheet",
+   "fieldtype": "Float",
+   "label": "Max working hours against Timesheet",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
+   "fieldname": "include_holidays_in_total_working_days",
+   "fieldtype": "Check",
+   "label": "Include holidays in Total no. of Working Days",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "description": "If checked, hides and disables Rounded Total field in Salary Slips",
+   "fieldname": "disable_rounded_total",
+   "fieldtype": "Check",
+   "label": "Disable Rounded Total",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0.5",
+   "description": "The fraction of daily wages to be paid for half-day attendance",
+   "fieldname": "daily_wages_fraction_for_half_day",
+   "fieldtype": "Float",
+   "label": "Daily Wages Fraction for Half Day",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "1",
+   "description": "Emails salary slip to employee based on preferred email selected in Employee",
+   "fieldname": "email_salary_slip_to_employee",
+   "fieldtype": "Check",
+   "label": "Email Salary Slip to Employee",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval: doc.email_salary_slip_to_employee == 1;",
+   "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",
+   "fieldname": "encrypt_salary_slips_in_emails",
+   "fieldtype": "Check",
+   "label": "Encrypt Salary Slips in Emails",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1",
+   "description": "<b>Example:</b> SAL-{first_name}-{date_of_birth.year} <br>This will generate a password like SAL-Jane-1972",
+   "fieldname": "password_policy",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Password Policy",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval:doc.payroll_based_on == 'Attendance'",
+   "fieldname": "consider_unmarked_attendance_as",
+   "fieldtype": "Select",
+   "label": "Consider Unmarked Attendance As",
+   "options": "Present\nAbsent",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "icon": "fa fa-cog",
+ "issingle": 1,
+ "links": [],
+ "modified": "2020-06-05 12:35:34.861674",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "ASC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
new file mode 100644
index 0000000..5efa41d
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.utils import cint
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
+from frappe import _
+
+class PayrollSettings(Document):
+	def validate(self):
+		self.validate_password_policy()
+
+		if not self.daily_wages_fraction_for_half_day:
+			self.daily_wages_fraction_for_half_day = 0.5
+
+	def validate_password_policy(self):
+		if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails:
+			if not self.password_policy:
+				frappe.throw(_("Password policy for Salary Slips is not set"))
+
+
+	def on_update(self):
+		self.toggle_rounded_total()
+		frappe.clear_cache()
+
+	def toggle_rounded_total(self):
+		self.disable_rounded_total = cint(self.disable_rounded_total)
+		make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check")
+		make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check")
diff --git a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
new file mode 100644
index 0000000..314866e
--- /dev/null
+++ b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestPayrollSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/retention_bonus/__init__.py b/erpnext/payroll/doctype/retention_bonus/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/__init__.py
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js
new file mode 100644
index 0000000..64e726d
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Retention Bonus', {
+	setup: function(frm) {
+		frm.set_query("employee", function() {
+			return {
+				filters: {
+					"status": "Active"
+				}
+			};
+		});
+
+		frm.set_query("salary_component", function() {
+			return {
+				filters: {
+					"type": "Earning"
+				}
+			};
+		});
+	}
+});
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json
new file mode 100644
index 0000000..53fe17f
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json
@@ -0,0 +1,158 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "HR-RTB-.YYYY.-.#####",
+ "creation": "2018-05-13 14:59:42.038964",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "employee",
+  "bonus_payment_date",
+  "bonus_amount",
+  "salary_component",
+  "amended_from",
+  "column_break_6",
+  "employee_name",
+  "department",
+  "date_of_joining"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1
+  },
+  {
+   "fieldname": "bonus_payment_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Bonus Payment Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "bonus_amount",
+   "fieldtype": "Currency",
+   "label": "Bonus Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Retention Bonus",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
+  {
+   "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.date_of_joining",
+   "fieldname": "date_of_joining",
+   "fieldtype": "Data",
+   "label": "Date of Joining",
+   "read_only": 1
+  },
+  {
+   "fieldname": "salary_component",
+   "fieldtype": "Link",
+   "label": "Salary Component",
+   "options": "Salary Component",
+   "reqd": 1
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 22:42:05.251951",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Retention Bonus",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Employee",
+   "share": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
new file mode 100644
index 0000000..ed0d36c
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import getdate
+
+class RetentionBonus(Document):
+	def validate(self):
+		if frappe.get_value('Employee', self.employee, 'status') == 'Left':
+			frappe.throw(_('Cannot create Retention Bonus for left Employees'))
+		if getdate(self.bonus_payment_date) < getdate():
+			frappe.throw(_('Bonus Payment Date cannot be a past date'))
+
+	def on_submit(self):
+		company = frappe.db.get_value('Employee', self.employee, 'company')
+		additional_salary = self.get_additional_salary()
+
+		if not additional_salary:
+			additional_salary = frappe.new_doc('Additional Salary')
+			additional_salary.employee = self.employee
+			additional_salary.salary_component = self.salary_component
+			additional_salary.amount = self.bonus_amount
+			additional_salary.payroll_date = self.bonus_payment_date
+			additional_salary.company = company
+			additional_salary.ref_doctype = self.doctype
+			additional_salary.ref_docname = self.name
+			additional_salary.submit()
+			# self.db_set('additional_salary', additional_salary.name)
+
+		else:
+			bonus_added = frappe.db.get_value('Additional Salary', additional_salary, 'amount') + self.bonus_amount
+			frappe.db.set_value('Additional Salary', additional_salary, 'amount', bonus_added)
+			self.db_set('additional_salary', additional_salary)
+
+	def on_cancel(self):
+
+		additional_salary = self.get_additional_salary()
+		if self.additional_salary:
+			bonus_removed = frappe.db.get_value('Additional Salary', self.additional_salary, 'amount') - self.bonus_amount
+			if bonus_removed == 0:
+				frappe.get_doc('Additional Salary', self.additional_salary).cancel()
+			else:
+				frappe.db.set_value('Additional Salary', self.additional_salary, 'amount', bonus_removed)
+
+			# self.db_set('additional_salary', '')
+
+	def get_additional_salary(self):
+		return frappe.db.exists('Additional Salary', {
+				'employee': self.employee,
+				'salary_component': self.salary_component,
+				'payroll_date': self.bonus_payment_date,
+				'company': company,
+				'docstatus': 1,
+				'ref_doctype': self.doctype,
+				'ref_docname': self.name
+			})
diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
new file mode 100644
index 0000000..a4b95d3
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Retention Bonus", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Retention Bonus
+		() => frappe.tests.make('Retention Bonus', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
new file mode 100644
index 0000000..eef4f14
--- /dev/null
+++ b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestRetentionBonus(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/salary_component/README.md b/erpnext/payroll/doctype/salary_component/README.md
new file mode 100644
index 0000000..9644192
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/README.md
@@ -0,0 +1 @@
+Type of earning and deductions that is a part of the salary.
diff --git a/erpnext/payroll/doctype/salary_component/__init__.py b/erpnext/payroll/doctype/salary_component/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/__init__.py
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js
new file mode 100644
index 0000000..c455eb3
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/salary_component.js
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Salary Component', {
+	setup: function(frm) {
+		frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
+			var d = locals[cdt][cdn];
+			return {
+				filters: {
+					"is_group": 0,
+					"company": d.company
+				}
+			};
+		});
+		frm.set_query("earning_component_group", function() {
+			return {
+				filters: {
+					"is_group": 1,
+					"is_flexible_benefit": 1
+				}
+			};
+		});
+	},
+	is_flexible_benefit: function(frm) {
+		if(frm.doc.is_flexible_benefit){
+			set_value_for_condition_and_formula(frm);
+			frm.set_value("formula", '');
+			frm.set_value("amount", 0);
+		}
+	},
+	type: function(frm) {
+		if(frm.doc.type=="Earning"){
+			frm.set_value("is_tax_applicable", 1);
+			frm.set_value("variable_based_on_taxable_salary", 0);
+		}
+		if(frm.doc.type=="Deduction"){
+			frm.set_value("is_tax_applicable", 0);
+			frm.set_value("is_flexible_benefit", 0);
+		}
+	},
+	variable_based_on_taxable_salary: function(frm) {
+		if(frm.doc.variable_based_on_taxable_salary){
+			set_value_for_condition_and_formula(frm);
+		}
+	},
+	create_separate_payment_entry_against_benefit_claim: function(frm) {
+		if(frm.doc.create_separate_payment_entry_against_benefit_claim){
+			frm.set_df_property("accounts", "reqd", 1);
+			frm.set_value("only_tax_impact", 0);
+		}
+		else{
+			frm.set_df_property("accounts", "reqd", 0);
+		}
+	},
+	only_tax_impact: function(frm) {
+		if(frm.only_tax_impact){
+			frm.set_value("create_separate_payment_entry_against_benefit_claim", 0);
+		}
+	}
+});
+
+var set_value_for_condition_and_formula = function(frm) {
+	frm.set_value("formula", null);
+	frm.set_value("condition", null);
+	frm.set_value("amount_based_on_formula", 0);
+	frm.set_value("statistical_component", 0);
+	frm.set_value("do_not_include_in_total", 0);
+	frm.set_value("depends_on_payment_days", 0);
+};
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.json b/erpnext/payroll/doctype/salary_component/salary_component.json
new file mode 100644
index 0000000..f1e5cf0
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/salary_component.json
@@ -0,0 +1,273 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:salary_component",
+ "creation": "2016-06-30 15:42:43.631931",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "salary_component",
+  "salary_component_abbr",
+  "type",
+  "description",
+  "column_break_4",
+  "depends_on_payment_days",
+  "is_tax_applicable",
+  "is_income_tax_component",
+  "deduct_full_tax_on_selected_payroll_date",
+  "variable_based_on_taxable_salary",
+  "exempted_from_income_tax",
+  "round_to_the_nearest_integer",
+  "statistical_component",
+  "do_not_include_in_total",
+  "disabled",
+  "flexible_benefits",
+  "is_flexible_benefit",
+  "max_benefit_amount",
+  "column_break_9",
+  "pay_against_benefit_claim",
+  "only_tax_impact",
+  "create_separate_payment_entry_against_benefit_claim",
+  "section_break_5",
+  "accounts",
+  "condition_and_formula",
+  "condition",
+  "amount",
+  "amount_based_on_formula",
+  "formula",
+  "column_break_28",
+  "help"
+ ],
+ "fields": [
+  {
+   "fieldname": "salary_component",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Name",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "salary_component_abbr",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Abbr",
+   "print_width": "120px",
+   "reqd": 1,
+   "width": "120px"
+  },
+  {
+   "fieldname": "type",
+   "fieldtype": "Select",
+   "in_standard_filter": 1,
+   "label": "Type",
+   "options": "Earning\nDeduction",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "depends_on": "eval:doc.type == \"Earning\"",
+   "fieldname": "is_tax_applicable",
+   "fieldtype": "Check",
+   "label": "Is Tax Applicable"
+  },
+  {
+   "default": "1",
+   "fieldname": "depends_on_payment_days",
+   "fieldtype": "Check",
+   "label": "Depends on Payment Days",
+   "print_hide": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "do_not_include_in_total",
+   "fieldtype": "Check",
+   "label": "Do Not Include in Total"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.is_tax_applicable && doc.type=='Earning'",
+   "fieldname": "deduct_full_tax_on_selected_payroll_date",
+   "fieldtype": "Check",
+   "label": "Deduct Full Tax on Selected Payroll Date"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "label": "Disabled"
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "in_list_view": 1,
+   "label": "Description"
+  },
+  {
+   "default": "0",
+   "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
+   "fieldname": "statistical_component",
+   "fieldtype": "Check",
+   "label": "Statistical Component"
+  },
+  {
+   "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
+   "fieldname": "flexible_benefits",
+   "fieldtype": "Section Break",
+   "label": "Flexible Benefits"
+  },
+  {
+   "default": "0",
+   "fieldname": "is_flexible_benefit",
+   "fieldtype": "Check",
+   "label": "Is Flexible Benefit"
+  },
+  {
+   "depends_on": "is_flexible_benefit",
+   "fieldname": "max_benefit_amount",
+   "fieldtype": "Currency",
+   "label": "Max Benefit Amount (Yearly)"
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "depends_on": "is_flexible_benefit",
+   "fieldname": "pay_against_benefit_claim",
+   "fieldtype": "Check",
+   "label": "Pay Against Benefit Claim"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
+   "fieldname": "only_tax_impact",
+   "fieldtype": "Check",
+   "label": "Only Tax Impact (Cannot Claim But Part of Taxable Income)"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
+   "fieldname": "create_separate_payment_entry_against_benefit_claim",
+   "fieldtype": "Check",
+   "label": "Create Separate Payment Entry Against Benefit Claim"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.type == \"Deduction\"",
+   "fieldname": "variable_based_on_taxable_salary",
+   "fieldtype": "Check",
+   "label": "Variable Based On Taxable Salary"
+  },
+  {
+   "depends_on": "eval:doc.statistical_component != 1",
+   "fieldname": "section_break_5",
+   "fieldtype": "Section Break",
+   "label": "Accounts"
+  },
+  {
+   "fieldname": "accounts",
+   "fieldtype": "Table",
+   "label": "Accounts",
+   "options": "Salary Component Account"
+  },
+  {
+   "collapsible": 1,
+   "depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
+   "fieldname": "condition_and_formula",
+   "fieldtype": "Section Break",
+   "label": "Condition and Formula"
+  },
+  {
+   "fieldname": "condition",
+   "fieldtype": "Code",
+   "label": "Condition"
+  },
+  {
+   "default": "0",
+   "fieldname": "amount_based_on_formula",
+   "fieldtype": "Check",
+   "label": "Amount based on formula"
+  },
+  {
+   "depends_on": "amount_based_on_formula",
+   "fieldname": "formula",
+   "fieldtype": "Code",
+   "label": "Formula"
+  },
+  {
+   "depends_on": "eval:doc.amount_based_on_formula!==1",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "label": "Amount"
+  },
+  {
+   "fieldname": "column_break_28",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "help",
+   "fieldtype": "HTML",
+   "label": "Help",
+   "options": "<h3>Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base &lt; 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS &gt; 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
+  },
+  {
+   "default": "0",
+   "fieldname": "round_to_the_nearest_integer",
+   "fieldtype": "Check",
+   "label": "Round to the Nearest Integer"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary",
+   "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.",
+   "fieldname": "exempted_from_income_tax",
+   "fieldtype": "Check",
+   "label": "Exempted from Income Tax"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.type == \"Deduction\"",
+   "fieldname": "is_income_tax_component",
+   "fieldtype": "Check",
+   "label": "Is Income Tax Component",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "icon": "fa fa-flag",
+ "links": [],
+ "modified": "2020-06-01 15:39:20.826565",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Component",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "read": 1,
+   "role": "Employee"
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py
new file mode 100644
index 0000000..7c92631
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/salary_component.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+from frappe.model.naming import append_number_if_name_exists
+
+class SalaryComponent(Document):
+	def validate(self):
+		self.validate_abbr()
+
+	def validate_abbr(self):
+		if not self.salary_component_abbr:
+			self.salary_component_abbr = ''.join([c[0] for c in
+				self.salary_component.split()]).upper()
+
+		self.salary_component_abbr = self.salary_component_abbr.strip()
+		self.salary_component_abbr = append_number_if_name_exists('Salary Component', self.salary_component_abbr,
+			'salary_component_abbr', separator='_', filters={"name": ["!=", self.name]})
diff --git a/erpnext/payroll/doctype/salary_component/test_records.json b/erpnext/payroll/doctype/salary_component/test_records.json
new file mode 100644
index 0000000..104b44f
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/test_records.json
@@ -0,0 +1,36 @@
+[
+	{
+		"doctype": "Salary Component",
+		"salary_component": "_Test Basic Salary",
+		"type": "Earning",
+		"is_tax_applicable": 1
+	},
+	{
+		"doctype": "Salary Component",
+		"salary_component": "_Test Allowance",
+		"type": "Earning",
+		"is_tax_applicable": 1
+	},
+	{
+		"doctype": "Salary Component",
+		"salary_component": "_Test Professional Tax",
+		"type": "Deduction"
+	},
+	{
+		"doctype": "Salary Component",
+		"salary_component": "_Test TDS",
+		"type": "Deduction"
+	},
+	{
+		"doctype": "Salary Component",
+		"salary_component": "Basic",
+		"type": "Earning",
+		"is_tax_applicable": 1
+	},
+	{
+		"doctype": "Salary Component",
+		"salary_component": "Leave Encashment",
+		"type": "Earning",
+		"is_tax_applicable": 1
+	}
+]
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.js b/erpnext/payroll/doctype/salary_component/test_salary_component.js
new file mode 100644
index 0000000..c47d32d
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/test_salary_component.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Salary Component", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Salary Component
+		() => frappe.tests.make('Salary Component', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py
new file mode 100644
index 0000000..4f7db0c
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_component/test_salary_component.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Salary Component')
+
+class TestSalaryComponent(unittest.TestCase):
+	pass
+
+
+def create_salary_component(component_name, **args):
+	if not frappe.db.exists("Salary Component", component_name):
+			frappe.get_doc({
+				"doctype": "Salary Component",
+				"salary_component": component_name,
+				"type": args.get("type") or "Earning",
+				"is_tax_applicable": args.get("is_tax_applicable") or 1
+			}).insert()
diff --git a/erpnext/payroll/doctype/salary_detail/__init__.py b/erpnext/payroll/doctype/salary_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_detail/__init__.py
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
new file mode 100644
index 0000000..b7d2bc1
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -0,0 +1,223 @@
+{
+ "actions": [],
+ "creation": "2016-06-30 15:32:36.385111",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "salary_component",
+  "abbr",
+  "statistical_component",
+  "column_break_3",
+  "deduct_full_tax_on_selected_payroll_date",
+  "depends_on_payment_days",
+  "is_tax_applicable",
+  "exempted_from_income_tax",
+  "is_flexible_benefit",
+  "variable_based_on_taxable_salary",
+  "section_break_2",
+  "condition",
+  "amount_based_on_formula",
+  "formula",
+  "amount",
+  "do_not_include_in_total",
+  "default_amount",
+  "additional_amount",
+  "tax_on_flexible_benefit",
+  "tax_on_additional_salary",
+  "section_break_11",
+  "additional_salary",
+  "condition_and_formula_help"
+ ],
+ "fields": [
+  {
+   "fieldname": "salary_component",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Component",
+   "options": "Salary Component",
+   "reqd": 1
+  },
+  {
+   "columns": 1,
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fetch_from": "salary_component.salary_component_abbr",
+   "fieldname": "abbr",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Abbr",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
+   "fetch_from": "salary_component.statistical_component",
+   "fieldname": "statistical_component",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Statistical Component"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.parentfield=='earnings'",
+   "fetch_from": "salary_component.is_tax_applicable",
+   "fieldname": "is_tax_applicable",
+   "fieldtype": "Check",
+   "label": "Is Tax Applicable",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.parentfield=='earnings'",
+   "fetch_from": "salary_component.is_flexible_benefit",
+   "fieldname": "is_flexible_benefit",
+   "fieldtype": "Check",
+   "label": "Is Flexible Benefit",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.parentfield=='deductions'",
+   "fetch_from": "salary_component.variable_based_on_taxable_salary",
+   "fieldname": "variable_based_on_taxable_salary",
+   "fieldtype": "Check",
+   "label": "Variable Based On Taxable Salary",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "salary_component.depends_on_payment_days",
+   "fieldname": "depends_on_payment_days",
+   "fieldtype": "Check",
+   "label": "Depends on Payment Days",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_full_tax_on_selected_payroll_date",
+   "fieldtype": "Check",
+   "label": "Deduct Full Tax on Selected Payroll Date",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.is_flexible_benefit != 1",
+   "fieldname": "section_break_2",
+   "fieldtype": "Section Break"
+  },
+  {
+   "allow_on_submit": 1,
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fieldname": "condition",
+   "fieldtype": "Code",
+   "label": "Condition"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fieldname": "amount_based_on_formula",
+   "fieldtype": "Check",
+   "label": "Amount based on formula"
+  },
+  {
+   "allow_on_submit": 1,
+   "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'",
+   "fieldname": "formula",
+   "fieldtype": "Code",
+   "in_list_view": 1,
+   "label": "Formula"
+  },
+  {
+   "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "default": "0",
+   "fieldname": "do_not_include_in_total",
+   "fieldtype": "Check",
+   "label": "Do not include in total"
+  },
+  {
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fieldname": "default_amount",
+   "fieldtype": "Currency",
+   "label": "Default Amount",
+   "options": "Company:company:default_currency",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "additional_amount",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Additional Amount",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1",
+   "fieldname": "tax_on_flexible_benefit",
+   "fieldtype": "Currency",
+   "label": "Tax on flexible benefit",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1",
+   "fieldname": "tax_on_additional_salary",
+   "fieldtype": "Currency",
+   "label": "Tax on additional salary",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fieldname": "section_break_11",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.parenttype=='Salary Structure'",
+   "fieldname": "condition_and_formula_help",
+   "fieldtype": "HTML",
+   "label": "Condition and Formula Help",
+   "options": "<h3>Condition and Formula Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base &lt; 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS &gt; 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
+  },
+  {
+   "fieldname": "additional_salary",
+   "fieldtype": "Link",
+   "label": "Additional Salary ",
+   "options": "Additional Salary"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.parentfield=='deductions'",
+   "fetch_from": "salary_component.exempted_from_income_tax",
+   "fieldname": "exempted_from_income_tax",
+   "fieldtype": "Check",
+   "label": "Exempted from Income Tax",
+   "read_only": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:21:26.300951",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py
new file mode 100644
index 0000000..0b18754
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class SalaryDetail(Document):
+	pass
diff --git a/erpnext/payroll/doctype/salary_slip/README.md b/erpnext/payroll/doctype/salary_slip/README.md
new file mode 100644
index 0000000..736550e
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/README.md
@@ -0,0 +1 @@
+Details of monthly salary paid for an Employee.
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/__init__.py b/erpnext/payroll/doctype/salary_slip/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
new file mode 100644
index 0000000..4b623e5
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -0,0 +1,168 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+cur_frm.add_fetch('employee', 'company', 'company');
+cur_frm.add_fetch('time_sheet', 'total_hours', 'working_hours');
+
+frappe.ui.form.on("Salary Slip", {
+	setup: function(frm) {
+		$.each(["earnings", "deductions"], function(i, table_fieldname) {
+			frm.get_field(table_fieldname).grid.editable_fields = [
+				{fieldname: 'salary_component', columns: 6},
+				{fieldname: 'amount', columns: 4}
+			];
+		});
+
+		frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
+			return {
+				filters: {
+					employee: frm.doc.employee
+				}
+			}
+		};
+
+		frm.set_query("salary_component", "earnings", function() {
+			return {
+				filters: {
+					type: "earning"
+				}
+			}
+		});
+
+		frm.set_query("salary_component", "deductions", function() {
+			return {
+				filters: {
+					type: "deduction"
+				}
+			}
+		});
+
+		frm.set_query("employee", function() {
+			return{
+				query: "erpnext.controllers.queries.employee_query"
+			}
+		});
+	},
+
+	start_date: function(frm){
+		if(frm.doc.start_date){
+			frm.trigger("set_end_date");
+		}
+	},
+
+	end_date: function(frm) {
+		frm.events.get_emp_and_working_day_details(frm);
+	},
+
+	set_end_date: function(frm){
+		frappe.call({
+			method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date',
+			args: {
+				frequency: frm.doc.payroll_frequency,
+				start_date: frm.doc.start_date
+			},
+			callback: function (r) {
+				if (r.message) {
+					frm.set_value('end_date', r.message.end_date);
+				}
+			}
+		})
+	},
+
+	company: function(frm) {
+		var company = locals[':Company'][frm.doc.company];
+		if(!frm.doc.letter_head && company.default_letter_head) {
+			frm.set_value('letter_head', company.default_letter_head);
+		}
+	},
+
+	refresh: function(frm) {
+		frm.trigger("toggle_fields")
+
+		var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
+		cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
+		cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
+	},
+
+	salary_slip_based_on_timesheet: function(frm) {
+		frm.trigger("toggle_fields");
+		frm.events.get_emp_and_working_day_details(frm);
+	},
+
+	payroll_frequency: function(frm) {
+		frm.trigger("toggle_fields");
+		frm.set_value('end_date', '');
+	},
+
+	employee: function(frm) {
+		frm.events.get_emp_and_working_day_details(frm);
+	},
+
+	leave_without_pay: function(frm){
+		if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
+			return frappe.call({
+				method: 'process_salary_based_on_working_days',
+				doc: frm.doc,
+				callback: function(r, rt) {
+					frm.refresh();
+				}
+			});
+		}
+	},
+
+	toggle_fields: function(frm) {
+		frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
+
+		frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
+			frm.doc.payroll_frequency != "");
+	},
+
+	get_emp_and_working_day_details: function(frm) {
+		return frappe.call({
+			method: 'get_emp_and_working_day_details',
+			doc: frm.doc,
+			callback: function(r, rt) {
+				frm.refresh();
+				if (frm.doc.absent_days){
+					frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true));
+				}
+			}
+		});
+	}
+})
+
+frappe.ui.form.on('Salary Slip Timesheet', {
+	time_sheet: function(frm, dt, dn) {
+		total_work_hours(frm, dt, dn);
+	},
+	timesheets_remove: function(frm, dt, dn) {
+		total_work_hours(frm, dt, dn);
+	}
+});
+
+// calculate total working hours, earnings based on hourly wages and totals
+var total_work_hours = function(frm, dt, dn) {
+	var total_working_hours = 0.0;
+	$.each(frm.doc["timesheets"] || [], function(i, timesheet) {
+		total_working_hours += timesheet.working_hours;
+	});
+	frm.set_value('total_working_hours', total_working_hours);
+
+	var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
+
+	frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
+		var gross_pay = 0.0;
+		$.each(frm.doc["earnings"], function(i, earning) {
+			if (earning.salary_component == r.salary_component) {
+				earning.amount = wages_amount;
+				frm.refresh_fields('earnings');
+			}
+			gross_pay += earning.amount;
+		});
+		frm.set_value('gross_pay', gross_pay);
+
+		frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction);
+		frm.doc.rounded_total = Math.round(frm.doc.net_pay);
+		refresh_many(['net_pay', 'rounded_total']);
+	});
+}
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
new file mode 100644
index 0000000..a6337de
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -0,0 +1,660 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "creation": "2013-01-10 16:34:15",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+  "posting_date",
+  "employee",
+  "employee_name",
+  "department",
+  "designation",
+  "branch",
+  "payroll_cost_center",
+  "column_break1",
+  "status",
+  "journal_entry",
+  "payroll_entry",
+  "company",
+  "letter_head",
+  "section_break_10",
+  "salary_slip_based_on_timesheet",
+  "start_date",
+  "end_date",
+  "salary_structure",
+  "payroll_frequency",
+  "column_break_15",
+  "total_working_days",
+  "unmarked_days",
+  "leave_without_pay",
+  "absent_days",
+  "payment_days",
+  "hourly_wages",
+  "timesheets",
+  "column_break_20",
+  "total_working_hours",
+  "hour_rate",
+  "section_break_26",
+  "bank_name",
+  "bank_account_no",
+  "mode_of_payment",
+  "section_break_32",
+  "deduct_tax_for_unclaimed_employee_benefits",
+  "deduct_tax_for_unsubmitted_tax_exemption_proof",
+  "earning_deduction",
+  "earning",
+  "earnings",
+  "deduction",
+  "deductions",
+  "totals",
+  "gross_pay",
+  "column_break_25",
+  "total_deduction",
+  "loan_repayment",
+  "loans",
+  "section_break_43",
+  "total_principal_amount",
+  "total_interest_amount",
+  "column_break_45",
+  "total_loan_repayment",
+  "net_pay_info",
+  "net_pay",
+  "column_break_53",
+  "rounded_total",
+  "section_break_55",
+  "total_in_words",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "default": "Today",
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Posting Date",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Employee",
+   "oldfieldname": "employee",
+   "oldfieldtype": "Link",
+   "options": "Employee",
+   "reqd": 1,
+   "search_index": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fetch_from": "employee.employee_name",
+   "fieldname": "employee_name",
+   "fieldtype": "Read Only",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "label": "Employee Name",
+   "oldfieldname": "employee_name",
+   "oldfieldtype": "Data",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fetch_from": "employee.department",
+   "fieldname": "department",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Department",
+   "oldfieldname": "department",
+   "oldfieldtype": "Link",
+   "options": "Department",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval:doc.designation",
+   "fetch_from": "employee.designation",
+   "fieldname": "designation",
+   "fieldtype": "Read Only",
+   "label": "Designation",
+   "oldfieldname": "designation",
+   "oldfieldtype": "Link",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fetch_from": "employee.branch",
+   "fieldname": "branch",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Branch",
+   "oldfieldname": "branch",
+   "oldfieldtype": "Link",
+   "options": "Branch",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break1",
+   "fieldtype": "Column Break",
+   "oldfieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1,
+   "width": "50%"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "options": "Draft\nSubmitted\nCancelled",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "journal_entry",
+   "fieldtype": "Link",
+   "label": "Journal Entry",
+   "options": "Journal Entry",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "payroll_entry",
+   "fieldtype": "Link",
+   "label": "Payroll Entry",
+   "options": "Payroll Entry",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Company",
+   "options": "Company",
+   "remember_last_selected_value": 1,
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "letter_head",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "label": "Letter Head",
+   "options": "Letter Head",
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_10",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "salary_slip_based_on_timesheet",
+   "fieldtype": "Check",
+   "label": "Salary Slip Based on Timesheet",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "start_date",
+   "fieldtype": "Date",
+   "label": "Start Date",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "end_date",
+   "fieldtype": "Date",
+   "label": "End Date",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_15",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "salary_structure",
+   "fieldtype": "Link",
+   "label": "Salary Structure",
+   "options": "Salary Structure",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
+   "fieldname": "payroll_frequency",
+   "fieldtype": "Select",
+   "label": "Payroll Frequency",
+   "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "total_working_days",
+   "fieldtype": "Float",
+   "label": "Working Days",
+   "oldfieldname": "total_days_in_month",
+   "oldfieldtype": "Int",
+   "read_only": 1,
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "leave_without_pay",
+   "fieldtype": "Float",
+   "label": "Leave Without Pay",
+   "oldfieldname": "leave_without_pay",
+   "oldfieldtype": "Currency",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "payment_days",
+   "fieldtype": "Float",
+   "label": "Payment Days",
+   "oldfieldname": "payment_days",
+   "oldfieldtype": "Float",
+   "read_only": 1,
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "hourly_wages",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "timesheets",
+   "fieldtype": "Table",
+   "label": "Salary Slip Timesheet",
+   "options": "Salary Slip Timesheet",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_20",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "total_working_hours",
+   "fieldtype": "Float",
+   "label": "Total Working Hours",
+   "print_hide_if_no_value": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "hour_rate",
+   "fieldtype": "Currency",
+   "label": "Hour Rate",
+   "options": "Company:company:default_currency",
+   "print_hide_if_no_value": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_26",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "bank_name",
+   "fieldtype": "Data",
+   "label": "Bank Name",
+   "oldfieldname": "bank_name",
+   "oldfieldtype": "Data",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "bank_account_no",
+   "fieldtype": "Data",
+   "label": "Bank Account No.",
+   "oldfieldname": "bank_account_no",
+   "oldfieldtype": "Data",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_32",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_tax_for_unclaimed_employee_benefits",
+   "fieldtype": "Check",
+   "label": "Deduct Tax For Unclaimed Employee Benefits",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof",
+   "fieldtype": "Check",
+   "label": "Deduct Tax For Unsubmitted Tax Exemption Proof",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "earning_deduction",
+   "fieldtype": "Section Break",
+   "label": "Earning & Deduction",
+   "oldfieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "earning",
+   "fieldtype": "Column Break",
+   "label": "Earning",
+   "oldfieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1,
+   "width": "50%"
+  },
+  {
+   "fieldname": "earnings",
+   "fieldtype": "Table",
+   "label": "Earnings",
+   "oldfieldname": "earning_details",
+   "oldfieldtype": "Table",
+   "options": "Salary Detail",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "deduction",
+   "fieldtype": "Column Break",
+   "label": "Deduction",
+   "oldfieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1,
+   "width": "50%"
+  },
+  {
+   "fieldname": "deductions",
+   "fieldtype": "Table",
+   "label": "Deductions",
+   "oldfieldname": "deduction_details",
+   "oldfieldtype": "Table",
+   "options": "Salary Detail",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "totals",
+   "fieldtype": "Section Break",
+   "oldfieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "gross_pay",
+   "fieldtype": "Currency",
+   "label": "Gross Pay",
+   "oldfieldname": "gross_pay",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_25",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "total_deduction",
+   "fieldtype": "Currency",
+   "label": "Total Deduction",
+   "oldfieldname": "total_deduction",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "depends_on": "total_loan_repayment",
+   "fieldname": "loan_repayment",
+   "fieldtype": "Section Break",
+   "label": "Loan repayment",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "loans",
+   "fieldtype": "Table",
+   "label": "Employee Loan",
+   "options": "Salary Slip Loan",
+   "print_hide": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_43",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "total_principal_amount",
+   "fieldtype": "Currency",
+   "label": "Total Principal Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "total_interest_amount",
+   "fieldtype": "Currency",
+   "label": "Total Interest Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_45",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "total_loan_repayment",
+   "fieldtype": "Currency",
+   "label": "Total Loan Repayment",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "net_pay_info",
+   "fieldtype": "Section Break",
+   "label": "net pay info",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "description": "Gross Pay - Total Deduction - Loan Repayment",
+   "fieldname": "net_pay",
+   "fieldtype": "Currency",
+   "label": "Net Pay",
+   "oldfieldname": "net_pay",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_53",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "bold": 1,
+   "fieldname": "rounded_total",
+   "fieldtype": "Currency",
+   "label": "Rounded Total",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "section_break_55",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "description": "Net Pay (in words) will be visible once you save the Salary Slip.",
+   "fieldname": "total_in_words",
+   "fieldtype": "Data",
+   "label": "Total in words",
+   "oldfieldname": "net_pay_in_words",
+   "oldfieldtype": "Data",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "ignore_user_permissions": 1,
+   "label": "Amended From",
+   "no_copy": 1,
+   "oldfieldname": "amended_from",
+   "oldfieldtype": "Data",
+   "options": "Salary Slip",
+   "print_hide": 1,
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fetch_from": "employee.payroll_cost_center",
+   "fetch_if_empty": 1,
+   "fieldname": "payroll_cost_center",
+   "fieldtype": "Link",
+   "label": "Payroll Cost Center",
+   "options": "Cost Center",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Select",
+   "label": "Mode Of Payment",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "absent_days",
+   "fieldtype": "Float",
+   "label": "Absent Days",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "unmarked_days",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Unmarked days",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "icon": "fa fa-file-text",
+ "idx": 9,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-06-05 14:42:43.921828",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Slip",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "read": 1,
+   "role": "Employee"
+  }
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "timeline_field": "employee",
+ "title_field": "employee_name"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
new file mode 100644
index 0000000..2da19b0
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -0,0 +1,1062 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+import datetime, math
+
+from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate
+from frappe.model.naming import make_autoname
+
+from frappe import msgprint, _
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
+from erpnext.utilities.transaction_base import TransactionBase
+from frappe.utils.background_jobs import enqueue
+from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component
+from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
+from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
+from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
+
+class SalarySlip(TransactionBase):
+	def __init__(self, *args, **kwargs):
+		super(SalarySlip, self).__init__(*args, **kwargs)
+		self.series = 'Sal Slip/{0}/.#####'.format(self.employee)
+		self.whitelisted_globals = {
+			"int": int,
+			"float": float,
+			"long": int,
+			"round": round,
+			"date": datetime.date,
+			"getdate": getdate
+		}
+
+	def autoname(self):
+		self.name = make_autoname(self.series)
+
+	def validate(self):
+		self.status = self.get_status()
+		self.validate_dates()
+		self.check_existing()
+		if not self.salary_slip_based_on_timesheet:
+			self.get_date_details()
+
+		if not (len(self.get("earnings")) or len(self.get("deductions"))):
+			# get details from salary structure
+			self.get_emp_and_working_day_details()
+		else:
+			self.get_working_days_details(lwp = self.leave_without_pay)
+
+		self.calculate_net_pay()
+
+		company_currency = erpnext.get_company_currency(self.company)
+		total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total
+		self.total_in_words = money_in_words(total, company_currency)
+
+		if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"):
+			max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet")
+			if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
+				frappe.msgprint(_("Total working hours should not be greater than max working hours {0}").
+								format(max_working_hours), alert=True)
+
+	def on_submit(self):
+		if self.net_pay < 0:
+			frappe.throw(_("Net Pay cannot be less than 0"))
+		else:
+			self.set_status()
+			self.update_status(self.name)
+			self.make_loan_repayment_entry()
+			if (frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
+				self.email_salary_slip()
+
+	def on_cancel(self):
+		self.set_status()
+		self.update_status()
+		self.cancel_loan_repayment_entry()
+
+	def on_trash(self):
+		from frappe.model.naming import revert_series_if_last
+		revert_series_if_last(self.series, self.name)
+
+	def get_status(self):
+		if self.docstatus == 0:
+			status = "Draft"
+		elif self.docstatus == 1:
+			status = "Submitted"
+		elif self.docstatus == 2:
+			status = "Cancelled"
+		return status
+
+	def validate_dates(self):
+		if date_diff(self.end_date, self.start_date) < 0:
+			frappe.throw(_("To date cannot be before From date"))
+
+	def is_rounding_total_disabled(self):
+		return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total"))
+
+	def check_existing(self):
+		if not self.salary_slip_based_on_timesheet:
+			ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
+						where start_date = %s and end_date = %s and docstatus != 2
+						and employee = %s and name != %s""",
+						(self.start_date, self.end_date, self.employee, self.name))
+			if ret_exist:
+				self.employee = ''
+				frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
+		else:
+			for data in self.timesheets:
+				if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
+					frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
+
+	def get_date_details(self):
+		if not self.end_date:
+			date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
+			self.start_date = date_details.start_date
+			self.end_date = date_details.end_date
+
+	def get_emp_and_working_day_details(self):
+		'''First time, load all the components from salary structure'''
+		if self.employee:
+			self.set("earnings", [])
+			self.set("deductions", [])
+
+			if not self.salary_slip_based_on_timesheet:
+				self.get_date_details()
+			self.validate_dates()
+			joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+				["date_of_joining", "relieving_date"])
+
+			#getin leave details
+			self.get_working_days_details(joining_date, relieving_date)
+			struct = self.check_sal_struct(joining_date, relieving_date)
+
+			if struct:
+				self._salary_structure_doc = frappe.get_doc('Salary Structure', struct)
+				self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
+				self.set_time_sheet()
+				self.pull_sal_struct()
+				consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present"
+				return consider_unmarked_attendance_as
+
+	def set_time_sheet(self):
+		if self.salary_slip_based_on_timesheet:
+			self.set("timesheets", [])
+			timesheets = frappe.db.sql(""" select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or
+				status = 'Billed')""", {'employee': self.employee, 'start_date': self.start_date, 'end_date': self.end_date}, as_dict=1)
+
+			for data in timesheets:
+				self.append('timesheets', {
+					'time_sheet': data.name,
+					'working_hours': data.total_hours
+				})
+
+	def check_sal_struct(self, joining_date, relieving_date):
+		cond = """and sa.employee=%(employee)s and (sa.from_date <= %(start_date)s or
+				sa.from_date <= %(end_date)s or sa.from_date <= %(joining_date)s)"""
+		if self.payroll_frequency:
+			cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
+
+		st_name = frappe.db.sql("""
+			select sa.salary_structure
+			from `tabSalary Structure Assignment` sa join `tabSalary Structure` ss
+			where sa.salary_structure=ss.name
+				and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s
+			order by sa.from_date desc
+			limit 1
+		""" %cond, {'employee': self.employee, 'start_date': self.start_date,
+			'end_date': self.end_date, 'joining_date': joining_date})
+
+		if st_name:
+			self.salary_structure = st_name[0][0]
+			return self.salary_structure
+
+		else:
+			self.salary_structure = None
+			frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
+				.format(self.employee), title=_('Salary Structure Missing'))
+
+	def pull_sal_struct(self):
+		from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+
+		if self.salary_slip_based_on_timesheet:
+			self.salary_structure = self._salary_structure_doc.name
+			self.hour_rate = self._salary_structure_doc.hour_rate
+			self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
+			wages_amount = self.hour_rate * self.total_working_hours
+
+			self.add_earning_for_hourly_wages(self, self._salary_structure_doc.salary_component, wages_amount)
+
+		make_salary_slip(self._salary_structure_doc.name, self)
+
+	def get_working_days_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0):
+		payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on")
+		include_holidays_in_total_working_days = frappe.db.get_single_value("Payroll Settings", "include_holidays_in_total_working_days")
+
+		working_days = date_diff(self.end_date, self.start_date) + 1
+		if for_preview:
+			self.total_working_days = working_days
+			self.payment_days = working_days
+			return
+
+		holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
+
+		if not cint(include_holidays_in_total_working_days):
+			working_days -= len(holidays)
+			if working_days < 0:
+				frappe.throw(_("There are more holidays than working days this month."))
+
+		if not payroll_based_on:
+			frappe.throw(_("Please set Payroll based on in HR settings"))
+
+		if payroll_based_on == "Attendance":
+			actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays)
+			self.absent_days = absent
+		else:
+			actual_lwp = self.calculate_lwp_based_on_leave_application(holidays, working_days)
+
+		if not lwp:
+			lwp = actual_lwp
+		elif lwp != actual_lwp:
+			frappe.msgprint(_("Leave Without Pay does not match with approved {} records")
+				.format(payroll_based_on))
+
+		self.leave_without_pay = lwp
+		self.total_working_days = working_days
+
+		payment_days = self.get_payment_days(joining_date,
+			relieving_date, include_holidays_in_total_working_days)
+
+		if flt(payment_days) > flt(lwp):
+			self.payment_days = flt(payment_days) - flt(lwp)
+
+			if payroll_based_on == "Attendance":
+				self.payment_days -= flt(absent)
+
+			unmarked_days = self.get_unmarked_days()
+			consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present"
+
+			if payroll_based_on == "Attendance" and consider_unmarked_attendance_as =="Absent":
+				self.absent_days += unmarked_days #will be treated as absent
+				self.payment_days -= unmarked_days
+				if include_holidays_in_total_working_days:
+					self.absent_days -= len(holidays)
+					for holiday in holidays:
+						if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }):
+							self.payment_days += 1
+
+
+		else:
+			self.payment_days = 0
+
+	def get_unmarked_days(self):
+		marked_days = frappe.get_all("Attendance", filters = {
+					"attendance_date": ["between", ['2020-05-1',"2020-05-30"]],
+					"employee": 'HR-EMP-00003',
+					"docstatus": 1
+				}, fields = ["COUNT(*) as marked_days"])[0].marked_days
+
+		return self.total_working_days - marked_days
+
+
+	def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days):
+		if not joining_date:
+			joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+				["date_of_joining", "relieving_date"])
+
+		start_date = getdate(self.start_date)
+		if joining_date:
+			if getdate(self.start_date) <= joining_date <= getdate(self.end_date):
+				start_date = joining_date
+			elif joining_date > getdate(self.end_date):
+				return
+
+		end_date = getdate(self.end_date)
+		if relieving_date:
+			if getdate(self.start_date) <= relieving_date <= getdate(self.end_date):
+				end_date = relieving_date
+			elif relieving_date < getdate(self.start_date):
+				frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
+					.format(relieving_date))
+
+		payment_days = date_diff(end_date, start_date) + 1
+
+		if not cint(include_holidays_in_total_working_days):
+			holidays = self.get_holidays_for_employee(start_date, end_date)
+			payment_days -= len(holidays)
+
+		return payment_days
+
+	def get_holidays_for_employee(self, start_date, end_date):
+		holiday_list = get_holiday_list_for_employee(self.employee)
+		holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
+			where
+				parent=%(holiday_list)s
+				and holiday_date >= %(start_date)s
+				and holiday_date <= %(end_date)s''', {
+					"holiday_list": holiday_list,
+					"start_date": start_date,
+					"end_date": end_date
+				})
+
+		holidays = [cstr(i) for i in holidays]
+
+		return holidays
+
+	def calculate_lwp_based_on_leave_application(self, holidays, working_days):
+		lwp = 0
+		holidays = "','".join(holidays)
+		daily_wages_fraction_for_half_day = \
+			flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
+
+		for d in range(working_days):
+			dt = add_days(cstr(getdate(self.start_date)), d)
+			leave = frappe.db.sql("""
+				SELECT t1.name,
+					CASE WHEN (t1.half_day_date = %(dt)s or t1.to_date = t1.from_date)
+					THEN t1.half_day else 0 END
+				FROM `tabLeave Application` t1, `tabLeave Type` t2
+				WHERE t2.name = t1.leave_type
+				AND t2.is_lwp = 1
+				AND t1.docstatus = 1
+				AND t1.employee = %(employee)s
+				AND ifnull(t1.salary_slip, '') = ''
+				AND CASE
+					WHEN t2.include_holiday != 1
+						THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
+					WHEN t2.include_holiday
+						THEN %(dt)s between from_date and to_date
+					END
+				""".format(holidays), {"employee": self.employee, "dt": dt})
+
+			if leave:
+				is_half_day_leave = cint(leave[0][1])
+				lwp += (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1
+
+		return lwp
+
+	def calculate_lwp_and_absent_days_based_on_attendance(self, holidays):
+		lwp = 0
+		absent = 0
+
+		daily_wages_fraction_for_half_day = \
+			flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
+
+		lwp_leave_types = dict(frappe.get_all("Leave Type", {"is_lwp": 1}, ["name", "include_holiday"], as_list=1))
+		attendances = frappe.db.sql('''
+			SELECT attendance_date, status, leave_type
+			FROM `tabAttendance`
+			WHERE
+				status in ("Absent", "Half Day", "On leave")
+				AND employee = %s
+				AND docstatus = 1
+				AND attendance_date between %s and %s
+		''', values=(self.employee, self.start_date, self.end_date), as_dict=1)
+
+		for d in attendances:
+			if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in lwp_leave_types:
+				continue
+
+			if formatdate(d.attendance_date, "yyyy-mm-dd") in holidays:
+				if d.status == "Absent" or \
+					(d.leave_type and d.leave_type in lwp_leave_types and not lwp_leave_types[d.leave_type]):
+						continue
+
+			if d.status == "Half Day":
+				lwp += (1 - daily_wages_fraction_for_half_day)
+			elif d.status == "On Leave" and d.leave_type in lwp_leave_types:
+				lwp += 1
+			elif d.status == "Absent":
+				absent += 1
+
+		return lwp, absent
+
+	def add_earning_for_hourly_wages(self, doc, salary_component, amount):
+		row_exists = False
+		for row in doc.earnings:
+			if row.salary_component == salary_component:
+				row.amount = amount
+				row_exists = True
+				break
+
+		if not row_exists:
+			wages_row = {
+				"salary_component": salary_component,
+				"abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"),
+				"amount": self.hour_rate * self.total_working_hours,
+				"default_amount": 0.0,
+				"additional_amount": 0.0
+			}
+			doc.append('earnings', wages_row)
+
+	def calculate_net_pay(self):
+		if self.salary_structure:
+			self.calculate_component_amounts("earnings")
+		self.gross_pay = self.get_component_totals("earnings")
+
+		if self.salary_structure:
+			self.calculate_component_amounts("deductions")
+		self.total_deduction = self.get_component_totals("deductions")
+
+		self.set_loan_repayment()
+
+		self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
+		self.rounded_total = rounded(self.net_pay)
+
+	def calculate_component_amounts(self, component_type):
+		if not getattr(self, '_salary_structure_doc', None):
+			self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
+
+		payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
+
+		self.add_structure_components(component_type)
+		self.add_additional_salary_components(component_type)
+		if component_type == "earnings":
+			self.add_employee_benefits(payroll_period)
+		else:
+			self.add_tax_components(payroll_period)
+
+		self.set_component_amounts_based_on_payment_days(component_type)
+
+	def add_structure_components(self, component_type):
+		data = self.get_data_for_eval()
+		for struct_row in self._salary_structure_doc.get(component_type):
+			amount = self.eval_condition_and_formula(struct_row, data)
+			if amount and struct_row.statistical_component == 0:
+				self.update_component_row(struct_row, amount, component_type)
+
+	def get_data_for_eval(self):
+		'''Returns data for evaluating formula'''
+		data = frappe._dict()
+
+		data.update(frappe.get_doc("Salary Structure Assignment",
+			{"employee": self.employee, "salary_structure": self.salary_structure}).as_dict())
+
+		data.update(frappe.get_doc("Employee", self.employee).as_dict())
+		data.update(self.as_dict())
+
+		# set values for components
+		salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
+		for sc in salary_components:
+			data.setdefault(sc.salary_component_abbr, 0)
+
+		for key in ('earnings', 'deductions'):
+			for d in self.get(key):
+				data[d.abbr] = d.amount
+
+		return data
+
+	def eval_condition_and_formula(self, d, data):
+		try:
+			condition = d.condition.strip().replace("\n", " ") if d.condition else None
+			if condition:
+				if not frappe.safe_eval(condition, self.whitelisted_globals, data):
+					return None
+			amount = d.amount
+			if d.amount_based_on_formula:
+				formula = d.formula.strip().replace("\n", " ") if d.formula else None
+				if formula:
+					amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount"))
+			if amount:
+				data[d.abbr] = amount
+
+			return amount
+
+		except NameError as err:
+			frappe.throw(_("Name error: {0}").format(err))
+		except SyntaxError as err:
+			frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
+		except Exception as e:
+			frappe.throw(_("Error in formula or condition: {0}").format(e))
+			raise
+
+	def add_employee_benefits(self, payroll_period):
+		for struct_row in self._salary_structure_doc.get("earnings"):
+			if struct_row.is_flexible_benefit == 1:
+				if frappe.db.get_value("Salary Component", struct_row.salary_component, "pay_against_benefit_claim") != 1:
+					benefit_component_amount = get_benefit_component_amount(self.employee, self.start_date, self.end_date,
+						struct_row.salary_component, self._salary_structure_doc, self.payroll_frequency, payroll_period)
+					if benefit_component_amount:
+						self.update_component_row(struct_row, benefit_component_amount, "earnings")
+				else:
+					benefit_claim_amount = get_benefit_claim_amount(self.employee, self.start_date, self.end_date, struct_row.salary_component)
+					if benefit_claim_amount:
+						self.update_component_row(struct_row, benefit_claim_amount, "earnings")
+
+		self.adjust_benefits_in_last_payroll_period(payroll_period)
+
+	def adjust_benefits_in_last_payroll_period(self, payroll_period):
+		if payroll_period:
+			if (getdate(payroll_period.end_date) <= getdate(self.end_date)):
+				last_benefits = get_last_payroll_period_benefits(self.employee, self.start_date, self.end_date,
+					payroll_period, self._salary_structure_doc)
+				if last_benefits:
+					for last_benefit in last_benefits:
+						last_benefit = frappe._dict(last_benefit)
+						amount = last_benefit.amount
+						self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
+
+	def add_additional_salary_components(self, component_type):
+		salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
+			self.start_date, self.end_date, component_type)
+		if salary_components_details and additional_salary_details:
+			for additional_salary in additional_salary_details:
+				additional_salary =frappe._dict(additional_salary)
+				amount = additional_salary.amount
+				overwrite = additional_salary.overwrite
+				self.update_component_row(frappe._dict(salary_components_details[additional_salary.component]), amount,
+					component_type, overwrite=overwrite, additional_salary=additional_salary.name)
+
+	def add_tax_components(self, payroll_period):
+		# Calculate variable_based_on_taxable_salary after all components updated in salary slip
+		tax_components, other_deduction_components = [], []
+		for d in self._salary_structure_doc.get("deductions"):
+			if d.variable_based_on_taxable_salary == 1 and not d.formula and not flt(d.amount):
+				tax_components.append(d.salary_component)
+			else:
+				other_deduction_components.append(d.salary_component)
+
+		if not tax_components:
+			tax_components = [d.name for d in frappe.get_all("Salary Component", filters={"variable_based_on_taxable_salary": 1})
+				if d.name not in other_deduction_components]
+
+		for d in tax_components:
+			tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
+			tax_row = self.get_salary_slip_row(d)
+			self.update_component_row(tax_row, tax_amount, "deductions")
+
+	def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
+		component_row = None
+		for d in self.get(key):
+			if d.salary_component == struct_row.salary_component:
+				component_row = d
+		if not component_row or (struct_row.get("is_additional_component") and not overwrite):
+			if amount:
+				self.append(key, {
+					'amount': amount,
+					'default_amount': amount if not struct_row.get("is_additional_component") else 0,
+					'depends_on_payment_days' : struct_row.depends_on_payment_days,
+					'salary_component' : struct_row.salary_component,
+					'abbr' : struct_row.abbr,
+					'additional_salary': additional_salary,
+					'do_not_include_in_total' : struct_row.do_not_include_in_total,
+					'is_tax_applicable': struct_row.is_tax_applicable,
+					'is_flexible_benefit': struct_row.is_flexible_benefit,
+					'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
+					'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
+					'additional_amount': amount if struct_row.get("is_additional_component") else 0,
+					'exempted_from_income_tax': struct_row.exempted_from_income_tax
+				})
+		else:
+			if struct_row.get("is_additional_component"):
+				if overwrite:
+					component_row.additional_amount = amount - component_row.get("default_amount", 0)
+					component_row.additional_salary = additional_salary
+				else:
+					component_row.additional_amount = amount
+
+				if not overwrite and component_row.default_amount:
+					amount += component_row.default_amount
+			else:
+				component_row.default_amount = amount
+
+			component_row.amount = amount
+			component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date
+
+	def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
+		if not payroll_period:
+			frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
+				.format(tax_component))
+			return
+
+		# Deduct taxes forcefully for unsubmitted tax exemption proof and unclaimed benefits in the last period
+		if payroll_period.end_date <= getdate(self.end_date):
+			self.deduct_tax_for_unsubmitted_tax_exemption_proof = 1
+			self.deduct_tax_for_unclaimed_employee_benefits = 1
+
+		return self.calculate_variable_tax(payroll_period, tax_component)
+
+	def calculate_variable_tax(self, payroll_period, tax_component):
+		# get Tax slab from salary structure assignment for the employee and payroll period
+		tax_slab = self.get_income_tax_slabs(payroll_period)
+
+		# get remaining numbers of sub-period (period for which one salary is processed)
+		remaining_sub_periods = get_period_factor(self.employee,
+			self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
+		# get taxable_earnings, paid_taxes for previous period
+		previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
+			self.start_date, tax_slab.allow_tax_exemption)
+		previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component)
+
+		# get taxable_earnings for current period (all days)
+		current_taxable_earnings = self.get_taxable_earnings(tax_slab.allow_tax_exemption)
+		future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1)
+
+		# get taxable_earnings, addition_earnings for current actual payment days
+		current_taxable_earnings_for_payment_days = self.get_taxable_earnings(tax_slab.allow_tax_exemption, based_on_payment_days=1)
+		current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings
+		current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income
+		current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax
+
+		# Get taxable unclaimed benefits
+		unclaimed_taxable_benefits = 0
+		if self.deduct_tax_for_unclaimed_employee_benefits:
+			unclaimed_taxable_benefits = self.calculate_unclaimed_taxable_benefits(payroll_period)
+			unclaimed_taxable_benefits += current_taxable_earnings_for_payment_days.flexi_benefits
+
+		# Total exemption amount based on tax exemption declaration
+		total_exemption_amount = self.get_total_exemption_amount(payroll_period, tax_slab)
+
+		#Employee Other Incomes
+		other_incomes = self.get_income_form_other_sources(payroll_period) or 0.0
+
+		# Total taxable earnings including additional and other incomes
+		total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \
+			+ current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount
+
+		# Total taxable earnings without additional earnings with full tax
+		total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax
+
+		# Structured tax amount
+		total_structured_tax_amount = self.calculate_tax_by_tax_slab(
+			total_taxable_earnings_without_full_tax_addl_components, tax_slab)
+		current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
+
+		# Total taxable earnings with additional earnings with full tax
+		full_tax_on_additional_earnings = 0.0
+		if current_additional_earnings_with_full_tax:
+			total_tax_amount = self.calculate_tax_by_tax_slab(total_taxable_earnings, tax_slab)
+			full_tax_on_additional_earnings = total_tax_amount - total_structured_tax_amount
+
+		current_tax_amount = current_structured_tax_amount + full_tax_on_additional_earnings
+		if flt(current_tax_amount) < 0:
+			current_tax_amount = 0
+
+		return current_tax_amount
+
+	def get_income_tax_slabs(self, payroll_period):
+		income_tax_slab, ss_assignment_name = frappe.db.get_value("Salary Structure Assignment",
+			{"employee": self.employee, "salary_structure": self.salary_structure, "docstatus": 1}, ["income_tax_slab", 'name'])
+
+		if not income_tax_slab:
+			frappe.throw(_("Income Tax Slab not set in Salary Structure Assignment: {0}").format(ss_assignment_name))
+
+		income_tax_slab_doc = frappe.get_doc("Income Tax Slab", income_tax_slab)
+		if income_tax_slab_doc.disabled:
+			frappe.throw(_("Income Tax Slab: {0} is disabled").format(income_tax_slab))
+
+		if getdate(income_tax_slab_doc.effective_from) > getdate(payroll_period.start_date):
+			frappe.throw(_("Income Tax Slab must be effective on or before Payroll Period Start Date: {0}")
+				.format(payroll_period.start_date))
+
+		return income_tax_slab_doc
+
+
+	def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False):
+		taxable_earnings = frappe.db.sql("""
+			select sum(sd.amount)
+			from
+				`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
+			where
+				sd.parentfield='earnings'
+				and sd.is_tax_applicable=1
+				and is_flexible_benefit=0
+				and ss.docstatus=1
+				and ss.employee=%(employee)s
+				and ss.start_date between %(from_date)s and %(to_date)s
+				and ss.end_date between %(from_date)s and %(to_date)s
+			""", {
+				"employee": self.employee,
+				"from_date": start_date,
+				"to_date": end_date
+			})
+		taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0
+
+		exempted_amount = 0
+		if allow_tax_exemption:
+			exempted_amount = frappe.db.sql("""
+				select sum(sd.amount)
+				from
+					`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
+				where
+					sd.parentfield='deductions'
+					and sd.exempted_from_income_tax=1
+					and is_flexible_benefit=0
+					and ss.docstatus=1
+					and ss.employee=%(employee)s
+					and ss.start_date between %(from_date)s and %(to_date)s
+					and ss.end_date between %(from_date)s and %(to_date)s
+				""", {
+					"employee": self.employee,
+					"from_date": start_date,
+					"to_date": end_date
+				})
+			exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
+
+		return taxable_earnings - exempted_amount
+
+	def get_tax_paid_in_period(self, start_date, end_date, tax_component):
+		# find total_tax_paid, tax paid for benefit, additional_salary
+		total_tax_paid = flt(frappe.db.sql("""
+			select
+				sum(sd.amount)
+			from
+				`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
+			where
+				sd.parentfield='deductions'
+				and sd.salary_component=%(salary_component)s
+				and sd.variable_based_on_taxable_salary=1
+				and ss.docstatus=1
+				and ss.employee=%(employee)s
+				and ss.start_date between %(from_date)s and %(to_date)s
+				and ss.end_date between %(from_date)s and %(to_date)s
+		""", {
+			"salary_component": tax_component,
+			"employee": self.employee,
+			"from_date": start_date,
+			"to_date": end_date
+		})[0][0])
+
+		return total_tax_paid
+
+	def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0):
+		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+			["date_of_joining", "relieving_date"])
+
+		if not relieving_date:
+			relieving_date = getdate(self.end_date)
+
+		if not joining_date:
+			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
+
+		taxable_earnings = 0
+		additional_income = 0
+		additional_income_with_full_tax = 0
+		flexi_benefits = 0
+
+		for earning in self.earnings:
+			if based_on_payment_days:
+				amount, additional_amount = self.get_amount_based_on_payment_days(earning, joining_date, relieving_date)
+			else:
+				amount, additional_amount = earning.amount, earning.additional_amount
+
+			if earning.is_tax_applicable:
+				if additional_amount:
+					taxable_earnings += (amount - additional_amount)
+					additional_income += additional_amount
+					if earning.deduct_full_tax_on_selected_payroll_date:
+						additional_income_with_full_tax += additional_amount
+					continue
+
+				if earning.is_flexible_benefit:
+					flexi_benefits += amount
+				else:
+					taxable_earnings += amount
+
+		if allow_tax_exemption:
+			for ded in self.deductions:
+				if ded.exempted_from_income_tax:
+					amount = ded.amount
+					if based_on_payment_days:
+						amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
+					taxable_earnings -= flt(amount)
+
+		return frappe._dict({
+			"taxable_earnings": taxable_earnings,
+			"additional_income": additional_income,
+			"additional_income_with_full_tax": additional_income_with_full_tax,
+			"flexi_benefits": flexi_benefits
+		})
+
+	def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
+		amount, additional_amount = row.amount, row.additional_amount
+		if (self.salary_structure and
+			cint(row.depends_on_payment_days) and cint(self.total_working_days) and
+			(not self.salary_slip_based_on_timesheet or
+				getdate(self.start_date) < joining_date or
+				getdate(self.end_date) > relieving_date
+			)):
+			additional_amount = flt((flt(row.additional_amount) * flt(self.payment_days)
+				/ cint(self.total_working_days)), row.precision("additional_amount"))
+			amount = flt((flt(row.default_amount) * flt(self.payment_days)
+				/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
+
+		elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
+			amount, additional_amount = 0, 0
+		elif not row.amount:
+			amount = flt(row.default_amount) + flt(row.additional_amount)
+
+		# apply rounding
+		if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
+			amount, additional_amount = rounded(amount), rounded(additional_amount)
+
+		return amount, additional_amount
+
+	def calculate_unclaimed_taxable_benefits(self, payroll_period):
+		# get total sum of benefits paid
+		total_benefits_paid = flt(frappe.db.sql("""
+			select sum(sd.amount)
+			from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
+			where
+				sd.parentfield='earnings'
+				and sd.is_tax_applicable=1
+				and is_flexible_benefit=1
+				and ss.docstatus=1
+				and ss.employee=%(employee)s
+				and ss.start_date between %(start_date)s and %(end_date)s
+				and ss.end_date between %(start_date)s and %(end_date)s
+		""", {
+			"employee": self.employee,
+			"start_date": payroll_period.start_date,
+			"end_date": self.start_date
+		})[0][0])
+
+		# get total benefits claimed
+		total_benefits_claimed = flt(frappe.db.sql("""
+			select sum(claimed_amount)
+			from `tabEmployee Benefit Claim`
+			where
+				docstatus=1
+				and employee=%s
+				and claim_date between %s and %s
+		""", (self.employee, payroll_period.start_date, self.end_date))[0][0])
+
+		return total_benefits_paid - total_benefits_claimed
+
+	def get_total_exemption_amount(self, payroll_period, tax_slab):
+		total_exemption_amount = 0
+		if tax_slab.allow_tax_exemption:
+			if self.deduct_tax_for_unsubmitted_tax_exemption_proof:
+				exemption_proof = frappe.db.get_value("Employee Tax Exemption Proof Submission",
+					{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
+					["exemption_amount"])
+				if exemption_proof:
+					total_exemption_amount = exemption_proof
+			else:
+				declaration = frappe.db.get_value("Employee Tax Exemption Declaration",
+					{"employee": self.employee, "payroll_period": payroll_period.name, "docstatus": 1},
+					["total_exemption_amount"])
+				if declaration:
+					total_exemption_amount = declaration
+
+			total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount)
+
+		return total_exemption_amount
+
+	def get_income_form_other_sources(self, payroll_period):
+		return frappe.get_all("Employee Other Income",
+			filters={
+				"employee": self.employee,
+				"payroll_period": payroll_period.name,
+				"company": self.company,
+				"docstatus": 1
+			},
+			fields="SUM(amount) as total_amount"
+		)[0].total_amount
+
+	def calculate_tax_by_tax_slab(self, annual_taxable_earning, tax_slab):
+		data = self.get_data_for_eval()
+		data.update({"annual_taxable_earning": annual_taxable_earning})
+		tax_amount = 0
+		for slab in tax_slab.slabs:
+			if slab.condition and not self.eval_tax_slab_condition(slab.condition, data):
+				continue
+			if not slab.to_amount and annual_taxable_earning >= slab.from_amount:
+				tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
+				continue
+			if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount:
+				tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
+			elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount:
+				tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * .01
+
+		# other taxes and charges on income tax
+		for d in tax_slab.other_taxes_and_charges:
+			if flt(d.min_taxable_income) and flt(d.min_taxable_income) > tax_amount:
+				continue
+
+			if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount:
+				continue
+
+			tax_amount += tax_amount * flt(d.percent) / 100
+
+		return tax_amount
+
+	def eval_tax_slab_condition(self, condition, data):
+		try:
+			condition = condition.strip()
+			if condition:
+				return frappe.safe_eval(condition, self.whitelisted_globals, data)
+		except NameError as err:
+			frappe.throw(_("Name error: {0}").format(err))
+		except SyntaxError as err:
+			frappe.throw(_("Syntax error in condition: {0}").format(err))
+		except Exception as e:
+			frappe.throw(_("Error in formula or condition: {0}").format(e))
+			raise
+
+	def get_salary_slip_row(self, salary_component):
+		component = frappe.get_doc("Salary Component", salary_component)
+		# Data for update_component_row
+		struct_row = frappe._dict()
+		struct_row['depends_on_payment_days'] = component.depends_on_payment_days
+		struct_row['salary_component'] = component.name
+		struct_row['abbr'] = component.salary_component_abbr
+		struct_row['do_not_include_in_total'] = component.do_not_include_in_total
+		struct_row['is_tax_applicable'] = component.is_tax_applicable
+		struct_row['is_flexible_benefit'] = component.is_flexible_benefit
+		struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
+		return struct_row
+
+	def get_component_totals(self, component_type):
+		total = 0.0
+		for d in self.get(component_type):
+			if not d.do_not_include_in_total:
+				d.amount = flt(d.amount, d.precision("amount"))
+				total += d.amount
+		return total
+
+	def set_component_amounts_based_on_payment_days(self, component_type):
+		joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+			["date_of_joining", "relieving_date"])
+
+		if not relieving_date:
+			relieving_date = getdate(self.end_date)
+
+		if not joining_date:
+			frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
+
+		for d in self.get(component_type):
+			d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+
+	def set_loan_repayment(self):
+		self.total_loan_repayment = 0
+		self.total_interest_amount = 0
+		self.total_principal_amount = 0
+
+		if not self.get('loans'):
+			for loan in self.get_loan_details():
+
+				amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment")
+
+				if amounts['interest_amount'] or amounts['payable_principal_amount']:
+					self.append('loans', {
+						'loan': loan.name,
+						'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'],
+						'interest_amount': amounts['interest_amount'],
+						'principal_amount': amounts['payable_principal_amount'],
+						'loan_account': loan.loan_account,
+						'interest_income_account': loan.interest_income_account
+					})
+
+		for payment in self.get('loans'):
+			amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment")
+			total_amount = amounts['interest_amount'] + amounts['payable_principal_amount']
+			if payment.total_payment > total_amount:
+				frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2}
+					against loan {3}""").format(payment.idx, frappe.bold(payment.total_payment),
+					frappe.bold(total_amount), frappe.bold(payment.loan)))
+
+			self.total_interest_amount += payment.interest_amount
+			self.total_principal_amount += payment.principal_amount
+
+			self.total_loan_repayment += payment.total_payment
+
+	def get_loan_details(self):
+
+		return frappe.get_all("Loan",
+			fields=["name", "interest_income_account", "loan_account", "loan_type"],
+			filters = {
+				"applicant": self.employee,
+				"docstatus": 1,
+				"repay_from_salary": 1,
+			})
+
+	def make_loan_repayment_entry(self):
+		for loan in self.loans:
+			repayment_entry = create_repayment_entry(loan.loan, self.employee,
+				self.company, self.posting_date, loan.loan_type, "Regular Payment", loan.interest_amount,
+				loan.principal_amount, loan.total_payment)
+
+			repayment_entry.save()
+			repayment_entry.submit()
+
+			loan.loan_repayment_entry = repayment_entry.name
+
+	def cancel_loan_repayment_entry(self):
+		for loan in self.loans:
+			if loan.loan_repayment_entry:
+				repayment_entry = frappe.get_doc("Loan Repayment", loan.loan_repayment_entry)
+				repayment_entry.cancel()
+
+	def email_salary_slip(self):
+		receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
+		payroll_settings = frappe.get_single("Payroll Settings")
+		message = "Please see attachment"
+		password = None
+		if payroll_settings.encrypt_salary_slips_in_emails:
+			password = generate_password_for_pdf(payroll_settings.password_policy, self.employee)
+			message += """<br>Note: Your salary slip is password protected,
+				the password to unlock the PDF is of the format {0}. """.format(payroll_settings.password_policy)
+
+		if receiver:
+			email_args = {
+				"recipients": [receiver],
+				"message": _(message),
+				"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
+				"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)],
+				"reference_doctype": self.doctype,
+				"reference_name": self.name
+				}
+			if not frappe.flags.in_test:
+				enqueue(method=frappe.sendmail, queue='short', timeout=300, is_async=True, **email_args)
+			else:
+				frappe.sendmail(**email_args)
+		else:
+			msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
+
+	def update_status(self, salary_slip=None):
+		for data in self.timesheets:
+			if data.time_sheet:
+				timesheet = frappe.get_doc('Timesheet', data.time_sheet)
+				timesheet.salary_slip = salary_slip
+				timesheet.flags.ignore_validate_update_after_submit = True
+				timesheet.set_status()
+				timesheet.save()
+
+	def set_status(self, status=None):
+		'''Get and update status'''
+		if not status:
+			status = self.get_status()
+		self.db_set("status", status)
+
+
+	def process_salary_structure(self, for_preview=0):
+		'''Calculate salary after salary structure details have been updated'''
+		if not self.salary_slip_based_on_timesheet:
+			self.get_date_details()
+		self.pull_emp_details()
+		self.get_working_days_details(for_preview=for_preview)
+		self.calculate_net_pay()
+
+	def pull_emp_details(self):
+		emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1)
+		if emp:
+			self.mode_of_payment = emp.salary_mode
+			self.bank_name = emp.bank_name
+			self.bank_account_no = emp.bank_ac_no
+
+	def process_salary_based_on_working_days(self):
+		self.get_working_days_details(lwp=self.leave_without_pay)
+		self.calculate_net_pay()
+
+def unlink_ref_doc_from_salary_slip(ref_no):
+	linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
+	where journal_entry=%s and docstatus < 2""", (ref_no))
+	if linked_ss:
+		for ss in linked_ss:
+			ss_doc = frappe.get_doc("Salary Slip", ss)
+			frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
+
+def generate_password_for_pdf(policy_template, employee):
+	employee = frappe.get_doc("Employee", employee)
+	return policy_template.format(**employee.as_dict())
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip_list.js b/erpnext/payroll/doctype/salary_slip/salary_slip_list.js
new file mode 100644
index 0000000..33d5bd7
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip_list.js
@@ -0,0 +1,3 @@
+frappe.listview_settings['Salary Slip'] = {
+	add_fields: ["employee", "employee_name"],
+};
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
new file mode 100644
index 0000000..06a1c7d
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js
@@ -0,0 +1,55 @@
+QUnit.test("test salary slip", function(assert) {
+	assert.expect(6);
+	let done = assert.async();
+	let employee_name;
+
+	let salary_slip = (ename) => {
+		frappe.run_serially([
+			() => frappe.db.get_value('Employee', {'employee_name': ename}, 'name'),
+			(r) => {
+				employee_name = r.message.name;
+			},
+			() => {
+				// Creating a salary slip for a employee
+				frappe.tests.make('Salary Slip', [
+					{ employee: employee_name}
+				]);
+			},
+			() => frappe.timeout(3),
+			() => {
+			// To check if all the calculations are correctly done
+				if(ename === 'Test Employee 1')
+				{
+					assert.ok(cur_frm.doc.gross_pay==24000,
+						'Gross amount for first employee is correctly calculated');
+					assert.ok(cur_frm.doc.total_deduction==4800,
+						'Deduction amount for first employee is correctly calculated');
+					assert.ok(cur_frm.doc.net_pay==19200,
+						'Net amount for first employee is correctly calculated');
+				}
+				if(ename === 'Test Employee 3')
+				{
+					assert.ok(cur_frm.doc.gross_pay==28800,
+						'Gross amount for second employee is correctly calculated');
+					assert.ok(cur_frm.doc.total_deduction==5760,
+						'Deduction amount for second employee is correctly calculated');
+					assert.ok(cur_frm.doc.net_pay==23040,
+						'Net amount for second employee is correctly calculated');
+				}
+			},
+		]);
+	};
+	frappe.run_serially([
+		() => salary_slip('Test Employee 1'),
+		() => frappe.timeout(6),
+		() => salary_slip('Test Employee 3'),
+		() => frappe.timeout(5),
+		() => frappe.set_route('List', 'Salary Slip', 'List'),
+		() => frappe.timeout(2),
+		() => {$('.list-row-checkbox').click();},
+		() => frappe.timeout(2),
+		() => frappe.click_button('Delete'),
+		() => frappe.click_button('Yes'),
+		() => done()
+	]);
+});
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
new file mode 100644
index 0000000..f42b9ad
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -0,0 +1,718 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+from __future__ import unicode_literals
+
+import unittest
+import frappe
+import erpnext
+import calendar
+import random
+from erpnext.accounts.utils import get_fiscal_year
+from frappe.utils.make_random import get_random
+from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \
+	import create_payroll_period, create_exemption_category
+
+class TestSalarySlip(unittest.TestCase):
+	def setUp(self):
+		setup_test()
+	def tearDown(self):
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
+		frappe.set_user("Administrator")
+
+	def test_payment_days_based_on_attendance(self):
+		from erpnext.hr.doctype.attendance.attendance import mark_attendance
+		no_of_days = self.get_no_of_days()
+
+		# Payroll based on attendance
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
+		frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75)
+
+		emp_id = make_employee("test_for_attendance@salary.com")
+		frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
+
+		frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
+
+		month_start_date = get_first_day(nowdate())
+		month_end_date = get_last_day(nowdate())
+
+		first_sunday = frappe.db.sql("""
+			select holiday_date from `tabHoliday`
+			where parent = 'Salary Slip Test Holiday List'
+				and holiday_date between %s and %s
+			order by holiday_date
+		""", (month_start_date, month_end_date))[0][0]
+
+		mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp
+		mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
+		mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp
+		mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp
+		mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp
+		mark_attendance(emp_id, add_days(first_sunday, 7), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # invalid lwp
+
+		ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly")
+
+		self.assertEqual(ss.leave_without_pay, 1.25)
+		self.assertEqual(ss.absent_days, 1)
+
+		days_in_month = no_of_days[0]
+		no_of_holidays = no_of_days[1]
+
+		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25)
+
+		#Gross pay calculation based on attendances
+		gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days))
+
+		self.assertEqual(ss.gross_pay, gross_pay)
+
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+
+	def test_payment_days_based_on_leave_application(self):
+		no_of_days = self.get_no_of_days()
+
+		# Payroll based on attendance
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+
+		emp_id = make_employee("test_for_attendance@salary.com")
+		frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"})
+
+		frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
+
+		month_start_date = get_first_day(nowdate())
+		month_end_date = get_last_day(nowdate())
+
+		first_sunday = frappe.db.sql("""
+			select holiday_date from `tabHoliday`
+			where parent = 'Salary Slip Test Holiday List'
+				and holiday_date between %s and %s
+			order by holiday_date
+		""", (month_start_date, month_end_date))[0][0]
+
+		make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay")
+
+		ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly")
+
+		self.assertEqual(ss.leave_without_pay, 3)
+
+		days_in_month = no_of_days[0]
+		no_of_holidays = no_of_days[1]
+
+		self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 3)
+
+		#Gross pay calculation based on attendances
+		gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay))
+
+		self.assertEqual(ss.gross_pay, gross_pay)
+
+		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
+
+	def test_salary_slip_with_holidays_included(self):
+		no_of_days = self.get_no_of_days()
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
+		make_employee("test_employee@salary.com")
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
+		ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+
+		self.assertEqual(ss.total_working_days, no_of_days[0])
+		self.assertEqual(ss.payment_days, no_of_days[0])
+		self.assertEqual(ss.earnings[0].amount, 50000)
+		self.assertEqual(ss.earnings[1].amount, 3000)
+		self.assertEqual(ss.gross_pay, 78000)
+
+	def test_salary_slip_with_holidays_excluded(self):
+		no_of_days = self.get_no_of_days()
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
+		make_employee("test_employee@salary.com")
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
+		ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+
+		self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
+		self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
+		self.assertEqual(ss.earnings[0].amount, 50000)
+		self.assertEqual(ss.earnings[0].default_amount, 50000)
+		self.assertEqual(ss.earnings[1].amount, 3000)
+		self.assertEqual(ss.gross_pay, 78000)
+
+	def test_payment_days(self):
+		no_of_days = self.get_no_of_days()
+		# Holidays not included in working days
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1)
+
+		# set joinng date in the same month
+		make_employee("test_employee@salary.com")
+		if getdate(nowdate()).day >= 15:
+			relieving_date = getdate(add_days(nowdate(),-10))
+			date_of_joining = getdate(add_days(nowdate(),-10))
+		elif getdate(nowdate()).day < 15 and getdate(nowdate()).day >= 5:
+			date_of_joining = getdate(add_days(nowdate(),-3))
+			relieving_date = getdate(add_days(nowdate(),-3))
+		elif getdate(nowdate()).day < 5 and not getdate(nowdate()).day == 1:
+			date_of_joining = getdate(add_days(nowdate(),-1))
+			relieving_date = getdate(add_days(nowdate(),-1))
+		elif getdate(nowdate()).day == 1:
+			date_of_joining = getdate(nowdate())
+			relieving_date = getdate(nowdate())
+
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", date_of_joining)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
+
+		ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+
+		self.assertEqual(ss.total_working_days, no_of_days[0])
+		self.assertEqual(ss.payment_days, (no_of_days[0] - getdate(date_of_joining).day + 1))
+
+		# set relieving date in the same month
+		frappe.db.set_value("Employee",frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", (add_days(nowdate(),-60)))
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "status", "Left")
+		ss.save()
+
+		self.assertEqual(ss.total_working_days, no_of_days[0])
+		self.assertEqual(ss.payment_days, getdate(relieving_date).day)
+
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
+		frappe.db.set_value("Employee", frappe.get_value("Employee",
+			{"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
+
+	def test_employee_salary_slip_read_permission(self):
+		make_employee("test_employee@salary.com")
+
+		salary_slip_test_employee = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+		frappe.set_user("test_employee@salary.com")
+		self.assertTrue(salary_slip_test_employee.has_permission("read"))
+
+	def test_email_salary_slip(self):
+		frappe.db.sql("delete from `tabEmail Queue`")
+
+		frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 1)
+
+		make_employee("test_employee@salary.com")
+		ss = make_employee_salary_slip("test_employee@salary.com", "Monthly")
+		ss.company = "_Test Company"
+		ss.save()
+		ss.submit()
+
+		email_queue = frappe.db.sql("""select name from `tabEmail Queue`""")
+		self.assertTrue(email_queue)
+
+	def test_loan_repayment_salary_slip(self):
+		from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts
+		from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
+
+		applicant = make_employee("test_loanemployee@salary.com", company="_Test Company")
+
+		create_loan_accounts()
+
+		create_loan_type("Car Loan", 500000, 8.4,
+			is_term_loan=1,
+			mode_of_payment='Cash',
+			payment_account='Payment Account - _TC',
+			loan_account='Loan Account - _TC',
+			interest_income_account='Interest Income Account - _TC',
+			penalty_income_account='Penalty Income Account - _TC')
+
+		loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
+		loan.repay_from_salary = 1
+		loan.submit()
+
+		make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
+
+		process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
+
+		ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly")
+		ss.submit()
+
+		self.assertEqual(ss.total_loan_repayment, 592)
+		self.assertEqual(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment))))
+
+	def test_payroll_frequency(self):
+		fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0]
+		month = "%02d" % getdate(nowdate()).month
+		m = get_month_details(fiscal_year, month)
+
+		for payroll_frequency in ["Monthly", "Bimonthly", "Fortnightly", "Weekly", "Daily"]:
+			make_employee(payroll_frequency + "_test_employee@salary.com")
+			ss = make_employee_salary_slip(payroll_frequency + "_test_employee@salary.com", payroll_frequency)
+			if payroll_frequency == "Monthly":
+				self.assertEqual(ss.end_date, m['month_end_date'])
+			elif payroll_frequency == "Bimonthly":
+				if getdate(ss.start_date).day <= 15:
+					self.assertEqual(ss.end_date, m['month_mid_end_date'])
+				else:
+					self.assertEqual(ss.end_date, m['month_end_date'])
+			elif payroll_frequency == "Fortnightly":
+				self.assertEqual(ss.end_date, add_days(nowdate(),13))
+			elif payroll_frequency == "Weekly":
+				self.assertEqual(ss.end_date, add_days(nowdate(),6))
+			elif payroll_frequency == "Daily":
+				self.assertEqual(ss.end_date, nowdate())
+
+	def test_tax_for_payroll_period(self):
+		data = {}
+		# test the impact of tax exemption declaration, tax exemption proof submission
+		# and deduct check boxes in annual tax calculation
+		# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
+		frappe.db.sql("""delete from `tabPayroll Period`""")
+		frappe.db.sql("""delete from `tabSalary Component`""")
+
+		payroll_period = create_payroll_period()
+
+		create_tax_slab(payroll_period, allow_tax_exemption=True)
+
+		employee = make_employee("test_tax@salary.slip")
+		delete_docs = [
+			"Salary Slip",
+			"Additional Salary",
+			"Employee Tax Exemption Declaration",
+			"Employee Tax Exemption Proof Submission",
+			"Employee Benefit Claim",
+			"Salary Structure Assignment"
+		]
+		for doc in delete_docs:
+			frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee))
+
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import \
+			make_salary_structure, create_salary_structure_assignment
+		salary_structure = make_salary_structure("Stucture to test tax", "Monthly",
+			other_details={"max_benefits": 100000}, test_tax=True)
+		create_salary_structure_assignment(employee, salary_structure.name,
+			payroll_period.start_date)
+
+		# create salary slip for whole period deducting tax only on last period
+		# to find the total tax amount paid
+		create_salary_slips_for_payroll_period(employee, salary_structure.name,
+			payroll_period, deduct_random=False)
+		tax_paid = get_tax_paid_in_period(employee)
+
+		annual_tax = 113589.0
+		try:
+			self.assertEqual(tax_paid, annual_tax)
+		except AssertionError:
+			print("\nSalary Slip - Annual tax calculation failed\n")
+			raise
+		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+
+		# create exemption declaration so the tax amount varies
+		create_exemption_declaration(employee, payroll_period.name)
+
+		# create for payroll deducting in random months
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
+			salary_structure.name, payroll_period)
+		tax_paid = get_tax_paid_in_period(employee)
+
+		# No proof, benefit claim sumitted, total tax paid, should not change
+		try:
+			self.assertEqual(tax_paid, annual_tax)
+		except AssertionError:
+			print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
+			raise
+
+		# Submit proof for total 120000
+		data["proof"] = create_proof_submission(employee, payroll_period, 120000)
+
+		# Submit benefit claim for total 50000
+		data["benefit-1"] = create_benefit_claim(employee, payroll_period, 15000, "Medical Allowance")
+		data["benefit-2"] = create_benefit_claim(employee, payroll_period, 35000, "Leave Travel Allowance")
+
+
+		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
+			salary_structure.name, payroll_period)
+		tax_paid = get_tax_paid_in_period(employee)
+
+		# total taxable income 416000, 166000 @ 5% ie. 8300
+		try:
+			self.assertEqual(tax_paid, 82389.0)
+		except AssertionError:
+			print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
+			raise
+
+		# create additional salary of 150000
+		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+		data["additional-1"] = create_additional_salary(employee, payroll_period, 50000)
+		data["additional-2"] = create_additional_salary(employee, payroll_period, 100000)
+		data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
+			salary_structure.name, payroll_period)
+
+		# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
+		tax_paid = get_tax_paid_in_period(employee)
+		try:
+			self.assertEqual(tax_paid, annual_tax)
+		except AssertionError:
+			print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
+			raise
+		frappe.db.sql("""delete from `tabAdditional Salary` where employee=%s""", (employee))
+
+		# undelete fixture data
+		frappe.db.rollback()
+
+	def make_activity_for_employee(self):
+		activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
+		activity_type.billing_rate = 50
+		activity_type.costing_rate = 20
+		activity_type.wage_rate = 25
+		activity_type.save()
+
+	def get_no_of_days(self):
+		no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year,
+			getdate(nowdate()).month)
+		no_of_holidays_in_month = len([1 for i in calendar.monthcalendar(getdate(nowdate()).year,
+			getdate(nowdate()).month) if i[6] != 0])
+
+		return [no_of_days_in_month[1], no_of_holidays_in_month]
+
+def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
+	from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+	if not salary_structure:
+		salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip"
+
+	employee = frappe.db.get_value("Employee", {"user_id": user})
+	salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee)
+	salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
+
+	if not salary_slip:
+		salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee)
+		salary_slip.employee_name = frappe.get_value("Employee",
+			{"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name")
+		salary_slip.payroll_frequency = payroll_frequency
+		salary_slip.posting_date = nowdate()
+		salary_slip.insert()
+
+	return salary_slip
+
+def make_salary_component(salary_components, test_tax, company_list=None):
+	for salary_component in salary_components:
+		if not frappe.db.exists('Salary Component', salary_component["salary_component"]):
+			if test_tax:
+				if salary_component["type"] == "Earning":
+					salary_component["is_tax_applicable"] = 1
+				elif salary_component["salary_component"] == "TDS":
+					salary_component["variable_based_on_taxable_salary"] = 1
+					salary_component["amount_based_on_formula"] = 0
+					salary_component["amount"] = 0
+					salary_component["formula"] = ""
+					salary_component["condition"] = ""
+			salary_component["doctype"] = "Salary Component"
+			salary_component["salary_component_abbr"] = salary_component["abbr"]
+			frappe.get_doc(salary_component).insert()
+		get_salary_component_account(salary_component["salary_component"], company_list)
+
+def get_salary_component_account(sal_comp, company_list=None):
+	company = erpnext.get_default_company()
+
+	if company_list and company not in company_list:
+		company_list.append(company)
+
+	sal_comp = frappe.get_doc("Salary Component", sal_comp)
+	if not sal_comp.get("accounts"):
+		for d in company_list:
+			company_abbr = frappe.get_cached_value('Company', d, 'abbr')
+
+			if sal_comp.type == "Earning":
+				account_name = "Salary"
+				parent_account = "Indirect Expenses - " + company_abbr
+			else:
+				account_name = "Salary Deductions"
+				parent_account = "Current Liabilities - " + company_abbr
+
+			sal_comp.append("accounts", {
+				"company": d,
+				"default_account": create_account(account_name, d, parent_account)
+			})
+			sal_comp.save()
+
+def create_account(account_name, company, parent_account):
+	company_abbr = frappe.get_cached_value('Company',  company,  'abbr')
+	account = frappe.db.get_value("Account", account_name + " - " + company_abbr)
+	if not account:
+		frappe.get_doc({
+			"doctype": "Account",
+			"account_name": account_name,
+			"parent_account": parent_account,
+			"company": company
+		}).insert()
+	return account
+
+def make_earning_salary_component(setup=False, test_tax=False, company_list=None):
+	data = [
+		{
+			"salary_component": 'Basic Salary',
+			"abbr":'BS',
+			"condition": 'base > 10000',
+			"formula": 'base',
+			"type": "Earning",
+			"amount_based_on_formula": 1
+		},
+		{
+			"salary_component": 'HRA',
+			"abbr":'H',
+			"amount": 3000,
+			"type": "Earning"
+		},
+		{
+			"salary_component": 'Special Allowance',
+			"abbr":'SA',
+			"condition": 'H < 10000',
+			"formula": 'BS*.5',
+			"type": "Earning",
+			"amount_based_on_formula": 1
+		},
+		{
+			"salary_component": "Leave Encashment",
+			"abbr": 'LE',
+			"type": "Earning"
+		}
+	]
+	if test_tax:
+		data.extend([
+			{
+				"salary_component": "Leave Travel Allowance",
+				"abbr": 'B',
+				"is_flexible_benefit": 1,
+				"type": "Earning",
+				"pay_against_benefit_claim": 1,
+				"max_benefit_amount": 100000,
+				"depends_on_payment_days": 0
+			},
+			{
+				"salary_component": "Medical Allowance",
+				"abbr": 'B',
+				"is_flexible_benefit": 1,
+				"pay_against_benefit_claim": 0,
+				"type": "Earning",
+				"max_benefit_amount": 15000
+			},
+			{
+				"salary_component": "Performance Bonus",
+				"abbr": 'B',
+				"type": "Earning"
+			}
+		])
+	if setup or test_tax:
+		make_salary_component(data, test_tax, company_list)
+	data.append({
+		"salary_component": 'Basic Salary',
+		"abbr":'BS',
+		"condition": 'base < 10000',
+		"formula": 'base*.2',
+		"type": "Earning",
+		"amount_based_on_formula": 1
+	})
+	return data
+
+def make_deduction_salary_component(setup=False, test_tax=False, company_list=None):
+	data =  [
+		{
+			"salary_component": 'Professional Tax',
+			"abbr":'PT',
+			"type": "Deduction",
+			"amount": 200,
+			"exempted_from_income_tax": 1
+
+		},
+		{
+			"salary_component": 'TDS',
+			"abbr":'T',
+			"type": "Deduction",
+			"depends_on_payment_days": 0,
+			"variable_based_on_taxable_salary": 1,
+			"round_to_the_nearest_integer": 1
+		}
+	]
+	if not test_tax:
+		data.append({
+			"salary_component": 'TDS',
+			"abbr":'T',
+			"condition": 'employment_type=="Intern"',
+			"type": "Deduction",
+			"round_to_the_nearest_integer": 1
+		})
+	if setup or test_tax:
+		make_salary_component(data, test_tax, company_list)
+
+	return data
+
+def get_tax_paid_in_period(employee):
+	tax_paid_amount = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail`
+		sd join `tabSalary Slip` ss where ss.name=sd.parent and ss.employee=%s
+		and ss.docstatus=1 and sd.salary_component='TDS'""", (employee))
+	return tax_paid_amount[0][0]
+
+def create_exemption_declaration(employee, payroll_period):
+	create_exemption_category()
+	declaration = frappe.get_doc({
+		"doctype": "Employee Tax Exemption Declaration",
+		"employee": employee,
+		"payroll_period": payroll_period,
+		"company": erpnext.get_default_company()
+	})
+	declaration.append("declarations", {
+		"exemption_sub_category": "_Test Sub Category",
+		"exemption_category": "_Test Category",
+		"amount": 100000
+	})
+	declaration.submit()
+
+def create_proof_submission(employee, payroll_period, amount):
+	submission_date = add_months(payroll_period.start_date, random.randint(0, 11))
+	proof_submission = frappe.get_doc({
+		"doctype": "Employee Tax Exemption Proof Submission",
+		"employee": employee,
+		"payroll_period": payroll_period.name,
+		"submission_date": submission_date
+	})
+	proof_submission.append("tax_exemption_proofs", {
+		"exemption_sub_category": "_Test Sub Category",
+		"exemption_category": "_Test Category",
+		"type_of_proof": "Test", "amount": amount
+	})
+	proof_submission.submit()
+	return submission_date
+
+def create_benefit_claim(employee, payroll_period, amount, component):
+	claim_date = add_months(payroll_period.start_date, random.randint(0, 11))
+	frappe.get_doc({
+		"doctype": "Employee Benefit Claim",
+		"employee": employee,
+		"claimed_amount": amount,
+		"claim_date": claim_date,
+		"earning_component": component
+	}).submit()
+	return claim_date
+
+def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption = False, dont_submit = False):
+	if frappe.db.exists("Income Tax Slab", "Tax Slab: " + payroll_period.name):
+		return
+
+	slabs = [
+		{
+			"from_amount": 250000,
+			"to_amount": 500000,
+			"percent_deduction": 5,
+			"condition": "annual_taxable_earning > 500000"
+		},
+		{
+			"from_amount": 500001,
+			"to_amount": 1000000,
+			"percent_deduction": 20
+		},
+		{
+			"from_amount": 1000001,
+			"percent_deduction": 30
+		}
+	]
+
+	income_tax_slab = frappe.new_doc("Income Tax Slab")
+	income_tax_slab.name = "Tax Slab: " + payroll_period.name
+	income_tax_slab.effective_from = effective_date or add_days(payroll_period.start_date, -2)
+
+	if allow_tax_exemption:
+		income_tax_slab.allow_tax_exemption = 1
+		income_tax_slab.standard_tax_exemption_amount = 50000
+
+	for item in slabs:
+		income_tax_slab.append("slabs", item)
+
+	income_tax_slab.append("other_taxes_and_charges", {
+		"description": "cess",
+		"percent": 4
+	})
+
+	income_tax_slab.save()
+	if not dont_submit:
+		income_tax_slab.submit()
+
+def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
+	deducted_dates = []
+	i = 0
+	while i < 12:
+		slip = frappe.get_doc({"doctype": "Salary Slip", "employee": employee,
+				"salary_structure": salary_structure, "frequency": "Monthly"})
+		if i == 0:
+			posting_date = add_days(payroll_period.start_date, 25)
+		else:
+			posting_date = add_months(posting_date, 1)
+		if i == 11:
+			slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1
+			slip.deduct_tax_for_unclaimed_employee_benefits = 1
+		if deduct_random and not random.randint(0, 2):
+			slip.deduct_tax_for_unsubmitted_tax_exemption_proof = 1
+			deducted_dates.append(posting_date)
+		slip.posting_date = posting_date
+		slip.start_date = get_first_day(posting_date)
+		slip.end_date = get_last_day(posting_date)
+		doc = make_salary_slip(salary_structure, slip, employee)
+		doc.submit()
+		i += 1
+	return deducted_dates
+
+def create_additional_salary(employee, payroll_period, amount):
+	salary_date = add_months(payroll_period.start_date, random.randint(0, 11))
+	frappe.get_doc({
+		"doctype": "Additional Salary",
+		"employee": employee,
+		"company": erpnext.get_default_company(),
+		"salary_component": "Performance Bonus",
+		"payroll_date": salary_date,
+		"amount": amount,
+		"type": "Earning"
+	}).submit()
+	return salary_date
+
+def make_leave_application(employee, from_date, to_date, leave_type, company=None):
+	leave_application = frappe.get_doc(dict(
+		doctype = 'Leave Application',
+		employee = employee,
+		leave_type = leave_type,
+		from_date = from_date,
+		to_date = to_date,
+		company = company or erpnext.get_default_company() or "_Test Company",
+		docstatus = 1,
+		status = "Approved",
+		leave_approver = 'test@example.com'
+	))
+	leave_application.submit()
+
+def setup_test():
+	make_earning_salary_component(setup=True, company_list=["_Test Company"])
+	make_deduction_salary_component(setup=True, company_list=["_Test Company"])
+
+	for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance", "Additional Salary"]:
+		frappe.db.sql("delete from `tab%s`" % dt)
+
+	make_holiday_list()
+
+	frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
+	frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
+	frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None)
+	frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None)
+
+def make_holiday_list():
+	fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
+	if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
+		holiday_list = frappe.get_doc({
+			"doctype": "Holiday List",
+			"holiday_list_name": "Salary Slip Test Holiday List",
+			"from_date": fiscal_year[1],
+			"to_date": fiscal_year[2],
+			"weekly_off": "Sunday"
+		}).insert()
+		holiday_list.get_weekly_off_dates()
+		holiday_list.save()
diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py b/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py
diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json
new file mode 100644
index 0000000..028c195
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "creation": "2016-06-14 19:22:29.811658",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "time_sheet",
+  "working_hours"
+ ],
+ "fields": [
+  {
+   "fieldname": "time_sheet",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Time Sheet",
+   "options": "Timesheet",
+   "reqd": 1
+  },
+  {
+   "fieldname": "working_hours",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Working Hours",
+   "no_copy": 1,
+   "read_only": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:27:43.463532",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Slip Timesheet",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
new file mode 100644
index 0000000..7adb12e
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class SalarySlipTimesheet(Document):
+	pass
diff --git a/erpnext/payroll/doctype/salary_structure/README.md b/erpnext/payroll/doctype/salary_structure/README.md
new file mode 100644
index 0000000..3795971
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/README.md
@@ -0,0 +1 @@
+Salary Template for an Employee, basis of which monthly Salary is calculated.
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure/__init__.py b/erpnext/payroll/doctype/salary_structure/__init__.py
new file mode 100644
index 0000000..baffc48
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/__init__.py
@@ -0,0 +1 @@
+from __future__ import unicode_literals
diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
new file mode 100644
index 0000000..e59d78d
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
@@ -0,0 +1,47 @@
+<h3>Variables</h3>
+<ul>
+    <li>
+        Variables from Salary Structure Assignment:<br>
+        <code>base = Base</code>, <code>variable = Variable</code> etc.
+    </li>
+    <li>
+        Variables from Employee:<br> <code>Employment Type = employment_type</code>, <code>Branch = branch</code> etc.
+    </li>
+    <li>
+        Variables Salary Slip:<br>
+        <code>Payment Days = payment_days</code>, <code>Leave without pay = leave_without_pay</code> etc.
+    </li>
+    <li>
+        Abbreviation from Salary Component:<br>
+        <code>BS = Basic Salary</code> etc.
+    </li>
+    <li>
+        Some additional variable:<br>
+        <code>gross_pay</code> and <code>annual_taxable_earning</code> can also be used.
+    </li>
+        <li>Direct Amount can also be used</li>
+</ul>
+
+<h3>Examples for Conditions and formula</h3>
+<ul>
+    <li>
+        Calculating Basic Salary based on <code>base</code>
+        <pre><code>Condition: base &lt; 10000</code></pre>
+        <pre><code>Formula: base * .2</code></pre>
+    </li>
+    <li>
+        Calculating HRA based on Basic Salary<code>BS</code>
+        <pre><code>Condition: BS &gt; 2000</code></pre>
+        <pre><code>Formula: BS * .1</code></pre>
+    </li>
+    <li>
+        Calculating TDS based on Employment Type<code>employment_type</code>
+        <pre><code>Condition: employment_type=="Intern"</code></pre>
+        <pre><code>Amount: 1000</code></pre>
+    </li>
+    <li>
+        Calculating Income Tax based on <code>annual_taxable_earning </code>
+        <pre><code>Condition: annual_taxable_earning > 20000000</code></pre>
+        <pre><code>Formula: annual_taxable_earning * 0.10 </code></pre>
+    </li>
+</ul>
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js
new file mode 100755
index 0000000..ca458f9
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js
@@ -0,0 +1,303 @@
+// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+{% include "erpnext/public/js/controllers/accounts.js" %}
+
+cur_frm.add_fetch('company', 'default_letter_head', 'letter_head');
+
+
+cur_frm.cscript.onload = function(doc, dt, dn){
+	var e_tbl = doc.earnings || [];
+	var d_tbl = doc.deductions || [];
+	if (e_tbl.length == 0 && d_tbl.length == 0)
+		return function(r, rt) { refresh_many(['earnings', 'deductions']);};
+}
+
+frappe.ui.form.on('Salary Structure', {
+	onload: function(frm) {
+
+		let help_button = $(`<a class = 'control-label'>
+			Condition and Formula Help
+		</a>`).click(()=>{
+
+			let d = new frappe.ui.Dialog({
+				title: 'Condition and Formula Help',
+				fields: [
+					{
+						fieldname: 'msg_wrapper',
+						fieldtype: 'HTML'
+					}
+				]
+			});
+
+			let message_html = frappe.render_template("condition_and_formula_help")
+
+			d.fields_dict.msg_wrapper.$wrapper.append(message_html)
+
+			d.show()
+		});
+		frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button)
+
+		frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet)
+
+		frm.set_query("salary_component", "earnings", function() {
+			return {
+				filters: {
+					type: "earning"
+				}
+			}
+		});
+		frm.set_query("salary_component", "deductions", function() {
+			return {
+				filters: {
+					type: "deduction"
+				}
+			}
+		});
+		frm.set_query("payment_account", function () {
+			var account_types = ["Bank", "Cash"];
+			return {
+				filters: {
+					"account_type": ["in", account_types],
+					"is_group": 0,
+					"company": frm.doc.company
+				}
+			};
+		});
+	},
+
+	refresh: function(frm) {
+		frm.trigger("toggle_fields");
+		frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
+		frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
+
+		if(frm.doc.docstatus === 1) {
+			frm.add_custom_button(__("Preview Salary Slip"), function() {
+				frm.trigger('preview_salary_slip');
+			});
+		}
+
+		if(frm.doc.docstatus==1) {
+			frm.add_custom_button(__("Assign Salary Structure"), function() {
+				var doc = frappe.model.get_new_doc('Salary Structure Assignment');
+				doc.salary_structure = frm.doc.name;
+				doc.company = frm.doc.company;
+				frappe.set_route('Form', 'Salary Structure Assignment', doc.name);
+			});
+			frm.add_custom_button(__("Assign to Employees"),function () {
+			frm.trigger('assign_to_employees')
+		})
+		}
+		let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"];
+		fields_read_only.forEach(function(field) {
+			frappe.meta.get_docfield("Salary Detail", field, frm.doc.name).read_only = 1;
+		});
+	},
+
+	assign_to_employees:function (frm) {
+		var d = new frappe.ui.Dialog({
+			title: __("Assign to Employees"),
+			fields: [
+				{fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")},
+				{fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1},
+				{fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")},
+				{fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')},
+				{fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')},
+                {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")},
+				{fieldname:'base_variable', fieldtype:'Section Break'},
+				{fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1},
+				{fieldname:'income_tax_slab', fieldtype:'Link', label: __('Income Tax Slab'), options: 'Income Tax Slab'},
+				{fieldname:'base_col_br', fieldtype:'Column Break'},
+				{fieldname:'base', fieldtype:'Currency', label: __('Base')},
+				{fieldname:'variable', fieldtype:'Currency', label: __('Variable')}
+			],
+			primary_action: function() {
+				var data = d.get_values();
+				frappe.call({
+					doc: frm.doc,
+					method: "assign_salary_structure",
+					args: data,
+					callback: function(r) {
+						if(!r.exc) {
+							d.hide();
+							frm.reload_doc();
+						}
+					}
+				});
+			},
+			primary_action_label: __('Assign')
+		});
+
+
+		d.show();
+	},
+
+	salary_slip_based_on_timesheet: function(frm) {
+		frm.trigger("toggle_fields")
+	},
+
+	preview_salary_slip: function(frm) {
+		frappe.call({
+			method: "erpnext.payroll.doctype.salary_structure.salary_structure.get_employees",
+			args: {
+				salary_structure: frm.doc.name
+			},
+			callback: function(r) {
+				var employees = r.message;
+				if(!employees) return;
+				if (employees.length == 1){
+					frm.events.open_salary_slip(frm, employees[0]);
+				} else {
+						var d = new frappe.ui.Dialog({
+						title: __("Preview Salary Slip"),
+						fields: [
+							{
+								"label":__("Employee"),
+								"fieldname":"employee",
+								"fieldtype":"Select",
+								"reqd": true,
+								options: employees
+							}, {
+								fieldname:"fetch",
+								"label":__("Show Salary Slip"),
+								"fieldtype":"Button"
+							}
+						]
+					});
+					d.get_input("fetch").on("click", function() {
+						var values = d.get_values();
+						if(!values) return;
+							frm.events.open_salary_slip(frm, values.employee)
+
+					});
+					d.show();
+				}
+			}
+		});
+	},
+
+	open_salary_slip: function(frm, employee){
+		var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard";
+		frappe.call({
+			method: "erpnext.payroll.doctype.salary_structure.salary_structure.make_salary_slip",
+			args: {
+				source_name: frm.doc.name,
+				employee: employee,
+				as_print: 1,
+				print_format: print_format,
+				for_preview: 1
+			},
+			callback: function(r) {
+				var new_window = window.open();
+				new_window.document.write(r.message);
+			}
+		});
+	},
+
+	toggle_fields: function(frm) {
+		frm.toggle_display(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
+		frm.toggle_reqd(['salary_component', 'hour_rate'], frm.doc.salary_slip_based_on_timesheet);
+		frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
+	}
+});
+
+var validate_date = function(frm, cdt, cdn) {
+	var doc = locals[cdt][cdn];
+	if(doc.to_date && doc.from_date) {
+		var from_date = frappe.datetime.str_to_obj(doc.from_date);
+		var to_date = frappe.datetime.str_to_obj(doc.to_date);
+
+		if(to_date < from_date) {
+			frappe.model.set_value(cdt, cdn, "to_date", "");
+			frappe.throw(__("From Date cannot be greater than To Date"));
+		}
+	}
+}
+
+
+cur_frm.cscript.amount = function(doc, cdt, cdn){
+	calculate_totals(doc, cdt, cdn);
+};
+
+var calculate_totals = function(doc) {
+	var tbl1 = doc.earnings || [];
+	var tbl2 = doc.deductions || [];
+
+	var total_earn = 0; var total_ded = 0;
+	for(var i = 0; i < tbl1.length; i++){
+		total_earn += flt(tbl1[i].amount);
+	}
+	for(var j = 0; j < tbl2.length; j++){
+		total_ded += flt(tbl2[j].amount);
+	}
+	doc.total_earning = total_earn;
+	doc.total_deduction = total_ded;
+	doc.net_pay = 0.0
+	if(doc.salary_slip_based_on_timesheet == 0){
+		doc.net_pay = flt(total_earn) - flt(total_ded);
+	}
+
+	refresh_many(['total_earning', 'total_deduction', 'net_pay']);
+}
+
+cur_frm.cscript.validate = function(doc, cdt, cdn) {
+	calculate_totals(doc);
+}
+
+
+frappe.ui.form.on('Salary Detail', {
+	amount: function(frm) {
+		calculate_totals(frm.doc);
+	},
+
+	earnings_remove: function(frm) {
+		calculate_totals(frm.doc);
+	},
+
+	deductions_remove: function(frm) {
+		calculate_totals(frm.doc);
+	},
+
+	salary_component: function(frm, cdt, cdn) {
+		var child = locals[cdt][cdn];
+		if(child.salary_component){
+			frappe.call({
+				method: "frappe.client.get",
+				args: {
+					doctype: "Salary Component",
+					name: child.salary_component
+				},
+				callback: function(data) {
+					if(data.message){
+						var result = data.message;
+						frappe.model.set_value(cdt, cdn, 'condition', result.condition);
+						frappe.model.set_value(cdt, cdn, 'amount_based_on_formula', result.amount_based_on_formula);
+						if(result.amount_based_on_formula == 1){
+							frappe.model.set_value(cdt, cdn, 'formula', result.formula);
+						}
+						else{
+							frappe.model.set_value(cdt, cdn, 'amount', result.amount);
+						}
+						frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component);
+						frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days);
+						frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total);
+						frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary);
+						frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable);
+						frappe.model.set_value(cdt, cdn, 'is_flexible_benefit', result.is_flexible_benefit);
+						refresh_field("earnings");
+						refresh_field("deductions");
+					}
+				}
+			});
+		}
+	},
+
+	amount_based_on_formula: function(frm, cdt, cdn) {
+		var child = locals[cdt][cdn];
+		if(child.amount_based_on_formula == 1){
+			frappe.model.set_value(cdt, cdn, 'amount', null);
+		}
+		else{
+			frappe.model.set_value(cdt, cdn, 'formula', null);
+		}
+	}
+})
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json
new file mode 100644
index 0000000..e710f6b
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json
@@ -0,0 +1,325 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "Prompt",
+ "creation": "2013-03-07 18:50:29",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "letter_head",
+  "column_break1",
+  "is_active",
+  "payroll_frequency",
+  "is_default",
+  "time_sheet_earning_detail",
+  "salary_slip_based_on_timesheet",
+  "column_break_17",
+  "salary_component",
+  "hour_rate",
+  "leave_encashment_amount_per_day",
+  "max_benefits",
+  "earning_deduction",
+  "earnings",
+  "deductions",
+  "conditions_and_formula_variable_and_example",
+  "net_pay_detail",
+  "column_break2",
+  "total_earning",
+  "total_deduction",
+  "net_pay",
+  "account",
+  "mode_of_payment",
+  "column_break_28",
+  "payment_account",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "remember_last_selected_value": 1,
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "letter_head",
+   "fieldtype": "Link",
+   "label": "Letter Head",
+   "options": "Letter Head",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break1",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1,
+   "width": "50%"
+  },
+  {
+   "default": "Yes",
+   "fieldname": "is_active",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Is Active",
+   "oldfieldname": "is_active",
+   "oldfieldtype": "Select",
+   "options": "\nYes\nNo",
+   "reqd": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "Monthly",
+   "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
+   "fieldname": "payroll_frequency",
+   "fieldtype": "Select",
+   "label": "Payroll Frequency",
+   "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "No",
+   "fieldname": "is_default",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Is Default",
+   "no_copy": 1,
+   "options": "Yes\nNo",
+   "print_hide": 1,
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "time_sheet_earning_detail",
+   "fieldtype": "Section Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "salary_slip_based_on_timesheet",
+   "fieldtype": "Check",
+   "label": "Salary Slip Based on Timesheet",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_17",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "description": "Salary Component for timesheet based payroll.",
+   "fieldname": "salary_component",
+   "fieldtype": "Link",
+   "label": "Salary Component",
+   "options": "Salary Component",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "hour_rate",
+   "fieldtype": "Currency",
+   "label": "Hour Rate",
+   "options": "Company:company:default_currency",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "leave_encashment_amount_per_day",
+   "fieldtype": "Currency",
+   "label": "Leave Encashment Amount Per Day",
+   "options": "Company:company:default_currency",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "max_benefits",
+   "fieldtype": "Currency",
+   "label": "Max Benefits (Amount)",
+   "options": "Company:company:default_currency",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "description": "Salary breakup based on Earning and Deduction.",
+   "fieldname": "earning_deduction",
+   "fieldtype": "Section Break",
+   "oldfieldname": "earning_deduction",
+   "oldfieldtype": "Section Break",
+   "precision": "2",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "earnings",
+   "fieldtype": "Table",
+   "label": "Earnings",
+   "oldfieldname": "earning_details",
+   "oldfieldtype": "Table",
+   "options": "Salary Detail",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "deductions",
+   "fieldtype": "Table",
+   "label": "Deductions",
+   "oldfieldname": "deduction_details",
+   "oldfieldtype": "Table",
+   "options": "Salary Detail",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "net_pay_detail",
+   "fieldtype": "Section Break",
+   "options": "Simple",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break2",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1,
+   "width": "50%"
+  },
+  {
+   "fieldname": "total_earning",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Total Earning",
+   "oldfieldname": "total_earning",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "total_deduction",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Total Deduction",
+   "oldfieldname": "total_deduction",
+   "oldfieldtype": "Currency",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "net_pay",
+   "fieldtype": "Currency",
+   "hidden": 1,
+   "label": "Net Pay",
+   "options": "Company:company:default_currency",
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "account",
+   "fieldtype": "Section Break",
+   "label": "Account",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "mode_of_payment",
+   "fieldtype": "Link",
+   "label": "Mode of Payment",
+   "options": "Mode of Payment",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "column_break_28",
+   "fieldtype": "Column Break",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "payment_account",
+   "fieldtype": "Link",
+   "label": "Payment Account",
+   "options": "Account",
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Salary Structure",
+   "print_hide": 1,
+   "read_only": 1,
+   "show_days": 1,
+   "show_seconds": 1
+  },
+  {
+   "fieldname": "conditions_and_formula_variable_and_example",
+   "fieldtype": "HTML",
+   "label": "Conditions and Formula variable and example",
+   "show_days": 1,
+   "show_seconds": 1
+  }
+ ],
+ "icon": "fa fa-file-text",
+ "idx": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-06-05 17:07:26.129355",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Structure",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "import": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py
new file mode 100644
index 0000000..ffc16d7
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+from frappe.utils import flt, cint, cstr
+from frappe import _
+from frappe.model.mapper import get_mapped_doc
+from frappe.model.document import Document
+from six import iteritems
+
+class SalaryStructure(Document):
+	def validate(self):
+		self.set_missing_values()
+		self.validate_amount()
+		self.strip_condition_and_formula_fields()
+		self.validate_max_benefits_with_flexi()
+		self.validate_component_based_on_tax_slab()
+
+	def set_missing_values(self):
+		overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
+		overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"]
+		for table in ["earnings", "deductions"]:
+			for d in self.get(table):
+				component_default_value = frappe.db.get_value("Salary Component", cstr(d.salary_component),
+					overwritten_fields + overwritten_fields_if_missing, as_dict=1)
+				if component_default_value:
+					for fieldname in overwritten_fields:
+						value = component_default_value.get(fieldname)
+						if d.get(fieldname) != value:
+							d.set(fieldname, value)
+
+					if not (d.get("amount") or d.get("formula")):
+						for fieldname in overwritten_fields_if_missing:
+							d.set(fieldname, component_default_value.get(fieldname))
+
+	def validate_component_based_on_tax_slab(self):
+		for row in self.deductions:
+			if row.variable_based_on_taxable_salary and (row.amount or row.formula):
+				frappe.throw(_("Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary")
+					.format(row.idx, row.salary_component))
+
+	def validate_amount(self):
+		if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
+			frappe.throw(_("Net pay cannot be negative"))
+
+	def strip_condition_and_formula_fields(self):
+		# remove whitespaces from condition and formula fields
+		for row in self.earnings:
+			row.condition = row.condition.strip() if row.condition else ""
+			row.formula = row.formula.strip() if row.formula else ""
+
+		for row in self.deductions:
+			row.condition = row.condition.strip() if row.condition else ""
+			row.formula = row.formula.strip() if row.formula else ""
+
+	def validate_max_benefits_with_flexi(self):
+		have_a_flexi = False
+		if self.earnings:
+			flexi_amount = 0
+			for earning_component in self.earnings:
+				if earning_component.is_flexible_benefit == 1:
+					have_a_flexi = True
+					max_of_component = frappe.db.get_value("Salary Component", earning_component.salary_component, "max_benefit_amount")
+					flexi_amount += max_of_component
+
+			if have_a_flexi and flt(self.max_benefits) == 0:
+				frappe.throw(_("Max benefits should be greater than zero to dispense benefits"))
+			if have_a_flexi and flexi_amount and flt(self.max_benefits) > flexi_amount:
+				frappe.throw(_("Total flexible benefit component amount {0} should not be less than max benefits {1}")
+					.format(flexi_amount, self.max_benefits))
+		if not have_a_flexi and flt(self.max_benefits) > 0:
+			frappe.throw(_("Salary Structure should have flexible benefit component(s) to dispense benefit amount"))
+
+	def get_employees(self, **kwargs):
+		conditions, values = [], []
+		for field, value in kwargs.items():
+			if value:
+				conditions.append("{0}=%s".format(field))
+				values.append(value)
+
+		condition_str = " and " + " and ".join(conditions) if conditions else ""
+
+		employees = frappe.db.sql_list("select name from tabEmployee where status='Active' {condition}"
+			.format(condition=condition_str), tuple(values))
+
+		return employees
+
+	@frappe.whitelist()
+	def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None,
+			from_date=None, base=None, variable=None, income_tax_slab=None):
+		employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee)
+
+		if employees:
+			if len(employees) > 20:
+				frappe.enqueue(assign_salary_structure_for_employees, timeout=600,
+					employees=employees, salary_structure=self,from_date=from_date,
+					base=base, variable=variable, income_tax_slab=income_tax_slab)
+			else:
+				assign_salary_structure_for_employees(employees, self, from_date=from_date,
+					base=base, variable=variable, income_tax_slab=income_tax_slab)
+		else:
+			frappe.msgprint(_("No Employee Found"))
+
+
+
+def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None, variable=None, income_tax_slab=None):
+	salary_structures_assignments = []
+	existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date)
+	count=0
+	for employee in employees:
+		if employee in existing_assignments_for:
+			continue
+		count +=1
+
+		salary_structures_assignment = create_salary_structures_assignment(employee,
+			salary_structure, from_date, base, variable, income_tax_slab)
+		salary_structures_assignments.append(salary_structures_assignment)
+		frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures..."))
+
+	if salary_structures_assignments:
+		frappe.msgprint(_("Structures have been assigned successfully"))
+
+
+def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable, income_tax_slab=None):
+	assignment = frappe.new_doc("Salary Structure Assignment")
+	assignment.employee = employee
+	assignment.salary_structure = salary_structure.name
+	assignment.company = salary_structure.company
+	assignment.from_date = from_date
+	assignment.base = base
+	assignment.variable = variable
+	assignment.income_tax_slab = income_tax_slab
+	assignment.save(ignore_permissions = True)
+	assignment.submit()
+	return assignment.name
+
+
+def get_existing_assignments(employees, salary_structure, from_date):
+	salary_structures_assignments = frappe.db.sql_list("""
+		select distinct employee from `tabSalary Structure Assignment`
+		where salary_structure=%s and employee in (%s)
+		and from_date=%s  and company= %s and docstatus=1
+	""" % ('%s', ', '.join(['%s']*len(employees)),'%s', '%s'), [salary_structure.name] + employees+[from_date]+[salary_structure.company])
+	if salary_structures_assignments:
+		frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}")
+			.format("\n".join(salary_structures_assignments)))
+	return salary_structures_assignments
+
+@frappe.whitelist()
+def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False):
+	def postprocess(source, target):
+		if employee:
+			employee_details = frappe.db.get_value("Employee", employee,
+				["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1)
+			target.employee = employee
+			target.employee_name = employee_details.employee_name
+			target.branch = employee_details.branch
+			target.designation = employee_details.designation
+			target.department = employee_details.department
+			target.payroll_cost_center = employee_details.payroll_cost_center
+			if not target.payroll_cost_center and target.department:
+				target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center")
+
+		target.run_method('process_salary_structure', for_preview=for_preview)
+
+	doc = get_mapped_doc("Salary Structure", source_name, {
+		"Salary Structure": {
+			"doctype": "Salary Slip",
+			"field_map": {
+				"total_earning": "gross_pay",
+				"name": "salary_structure"
+			}
+		}
+	}, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions)
+
+	if cint(as_print):
+		doc.name = 'Preview for {0}'.format(employee)
+		return frappe.get_print(doc.doctype, doc.name, doc = doc, print_format = print_format)
+	else:
+		return doc
+
+
+@frappe.whitelist()
+def get_employees(salary_structure):
+	employees = frappe.get_list('Salary Structure Assignment',
+		filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee'])
+
+	if not employees:
+		frappe.throw(_("There's no Employee with Salary Structure: {0}. \
+			Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure))
+
+	return list(set([d.employee for d in employees]))
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
new file mode 100644
index 0000000..547f2b8
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+	return {
+		'fieldname': 'salary_structure',
+		'non_standard_fieldnames': {
+			'Employee Grade': 'default_salary_structure'
+		},
+		'transactions': [
+			{
+				'items': ['Salary Structure Assignment', 'Salary Slip']
+			},
+			{
+				'items': ['Employee Grade']
+			},
+		]
+	}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.js b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js
new file mode 100644
index 0000000..542fa50
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js
@@ -0,0 +1,78 @@
+QUnit.test("test Salary Structure", function(assert) {
+	assert.expect(7);
+	let done = assert.async();
+	let employee_name1;
+
+	frappe.run_serially([
+		() => frappe.db.get_value('Employee', {'employee_name': "Test Employee 1"}, 'name',
+			(r) => {
+				employee_name1 = r.name;
+			}
+		),
+		() => frappe.timeout(5),
+		() => frappe.db.get_value('Employee', {'employee_name': "Test Employee 3"}, 'name',
+			(r) => {
+			// Creating Salary Structure for employees);
+				return frappe.tests.make('Salary Structure', [
+					{ __newname: 'Test Salary Structure'},
+					{ company: 'For Testing'},
+					{ payroll_frequency: 'Monthly'},
+					{ employees: [
+						[
+							{employee: employee_name1},
+							{from_date: '2017-07-01'},
+							{base: 25000}
+						],
+						[
+							{employee: r.name},
+							{from_date: '2017-07-01'},
+							{base: 30000}
+						]
+					]},
+					{ earnings: [
+						[
+							{salary_component: 'Basic'},
+							{formula: 'base * .80'}
+						],
+						[
+							{salary_component: 'Leave Encashment'},
+							{formula: 'B * .20'}
+						]
+					]},
+					{ deductions: [
+						[
+							{salary_component: 'Income Tax'},
+							{formula: '(B+LE) * .20'}
+						]
+					]},
+					{ payment_account: 'CASH - FT'},
+				]);
+			}
+		),
+		() => frappe.timeout(15),
+		() => {
+			// To check if all the fields are correctly set
+			assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1',
+				'Employee 1 name correctly set');
+
+			assert.ok(cur_frm.doc.employees[1].employee_name=='Test Employee 3',
+				'Employee 2 name correctly set');
+
+			assert.ok(cur_frm.doc.employees[0].base==25000,
+				'Base value for first employee is correctly set');
+
+			assert.ok(cur_frm.doc.employees[1].base==30000,
+				'Base value for second employee is correctly set');
+
+			assert.ok(cur_frm.doc.earnings[0].formula.includes('base * .80'),
+				'Formula for earnings as Basic is correctly set');
+
+			assert.ok(cur_frm.doc.earnings[1].formula.includes('B * .20'),
+				'Formula for earnings as Leave Encashment is correctly set');
+
+			assert.ok(cur_frm.doc.deductions[0].formula.includes('(B+LE) * .20'),
+				'Formula for deductions as Income Tax is correctly set');
+		},
+		() => done()
+	]);
+});
diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
new file mode 100644
index 0000000..e04fda8
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+import erpnext
+from frappe.utils.make_random import get_random
+from frappe.utils import nowdate, add_days, add_years, getdate, add_months
+from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\
+	make_deduction_salary_component, make_employee_salary_slip, create_tax_slab
+from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period
+
+
+test_dependencies = ["Fiscal Year"]
+
+class TestSalaryStructure(unittest.TestCase):
+	def setUp(self):
+		for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment"]:
+			frappe.db.sql("delete from `tab%s`" % dt)
+
+		self.make_holiday_list()
+		frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
+		make_employee("test_employee@salary.com")
+		make_employee("test_employee_2@salary.com")
+
+	def make_holiday_list(self):
+		if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
+			holiday_list = frappe.get_doc({
+				"doctype": "Holiday List",
+				"holiday_list_name": "Salary Structure Test Holiday List",
+				"from_date": nowdate(),
+				"to_date": add_years(nowdate(), 1),
+				"weekly_off": "Sunday"
+			}).insert()
+			holiday_list.get_weekly_off_dates()
+			holiday_list.save()
+
+	def test_salary_structure_deduction_based_on_gross_pay(self):
+
+		emp = make_employee("test_employee_3@salary.com")
+
+		sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True)
+
+		sal_struct.earnings = [sal_struct.earnings[0]]
+		sal_struct.earnings[0].amount_based_on_formula = 1
+		sal_struct.earnings[0].formula =  "base"
+
+		sal_struct.deductions = [sal_struct.deductions[0]]
+
+		sal_struct.deductions[0].amount_based_on_formula = 1
+		sal_struct.deductions[0].condition = "gross_pay > 100"
+		sal_struct.deductions[0].formula =  "gross_pay * 0.2"
+
+		sal_struct.submit()
+
+		assignment = create_salary_structure_assignment(emp, "Salary Structure 2")
+		ss = make_salary_slip(sal_struct.name, employee = emp)
+
+		self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount)
+
+	def test_amount_totals(self):
+		frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0)
+		sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
+		if not sal_slip:
+			sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
+			self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
+			self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
+			self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
+			self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
+			self.assertEqual(sal_slip.get("gross_pay"), 78000)
+			self.assertEqual(sal_slip.get("deductions")[0].amount, 200)
+			self.assertEqual(sal_slip.get("net_pay"), 78000 - sal_slip.get("total_deduction"))
+
+	def test_whitespaces_in_formula_conditions_fields(self):
+		salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
+
+		for row in salary_structure.earnings:
+			row.formula = "\n%s\n\n"%row.formula
+			row.condition = "\n%s\n\n"%row.condition
+
+		for row in salary_structure.deductions:
+			row.formula = "\n%s\n\n"%row.formula
+			row.condition = "\n%s\n\n"%row.condition
+
+		salary_structure.save()
+
+		for row in salary_structure.earnings:
+			self.assertFalse("\n" in row.formula or "\n" in row.condition)
+
+		for row in salary_structure.deductions:
+			self.assertFalse(("\n" in row.formula) or ("\n" in row.condition))
+
+	def test_salary_structures_assignment(self):
+		salary_structure = make_salary_structure("Salary Structure Sample", "Monthly")
+		employee = "test_assign_stucture@salary.com"
+		employee_doc_name = make_employee(employee)
+		# clear the already assigned stuctures
+		frappe.db.sql('''delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s ''',
+					  ("test_assign_stucture@salary.com",salary_structure.name))
+		#test structure_assignment
+		salary_structure.assign_salary_structure(employee=employee_doc_name,from_date='2013-01-01',base=5000,variable=200)
+		salary_structure_assignment = frappe.get_doc("Salary Structure Assignment",{'employee':employee_doc_name, 'from_date':'2013-01-01'})
+		self.assertEqual(salary_structure_assignment.docstatus, 1)
+		self.assertEqual(salary_structure_assignment.base, 5000)
+		self.assertEqual(salary_structure_assignment.variable, 200)
+
+def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None,
+	test_tax=False, company=None):
+	if test_tax:
+		frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure))
+
+	if not frappe.db.exists('Salary Structure', salary_structure):
+		details = {
+			"doctype": "Salary Structure",
+			"name": salary_structure,
+			"company": company or erpnext.get_default_company(),
+			"earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
+			"deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]),
+			"payroll_frequency": payroll_frequency,
+			"payment_account": get_random("Account")
+		}
+		if other_details and isinstance(other_details, dict):
+			details.update(other_details)
+		salary_structure_doc = frappe.get_doc(details)
+		salary_structure_doc.insert()
+		if not dont_submit:
+			salary_structure_doc.submit()
+
+	else:
+		salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure)
+
+	if employee and not frappe.db.get_value("Salary Structure Assignment",
+		{'employee':employee, 'docstatus': 1}) and salary_structure_doc.docstatus==1:
+			create_salary_structure_assignment(employee, salary_structure, company=company)
+
+	return salary_structure_doc
+
+def create_salary_structure_assignment(employee, salary_structure, from_date=None, company=None):
+	if frappe.db.exists("Salary Structure Assignment", {"employee": employee}):
+		frappe.db.sql("""delete from `tabSalary Structure Assignment` where employee=%s""",(employee))
+
+	payroll_period = create_payroll_period()
+	create_tax_slab(payroll_period, allow_tax_exemption=True)
+
+	salary_structure_assignment = frappe.new_doc("Salary Structure Assignment")
+	salary_structure_assignment.employee = employee
+	salary_structure_assignment.base = 50000
+	salary_structure_assignment.variable = 5000
+	salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1)
+	salary_structure_assignment.salary_structure = salary_structure
+	salary_structure_assignment.company = company or erpnext.get_default_company()
+	salary_structure_assignment.save(ignore_permissions=True)
+	salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period"
+	salary_structure_assignment.submit()
+	return salary_structure_assignment
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/__init__.py b/erpnext/payroll/doctype/salary_structure_assignment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/__init__.py
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
new file mode 100644
index 0000000..818e853
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
@@ -0,0 +1,56 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Salary Structure Assignment', {
+	setup: function(frm) {
+		frm.set_query("employee", function() {
+			return {
+				query: "erpnext.controllers.queries.employee_query",
+				filters: {
+					company: frm.doc.company
+				}
+			}
+		});
+		frm.set_query("salary_structure", function() {
+			return {
+				filters: {
+					company: frm.doc.company,
+					docstatus: 1,
+					is_active: "Yes"
+				}
+			}
+		});
+
+		frm.set_query("income_tax_slab", function() {
+			return {
+				filters: {
+					company: frm.doc.company,
+					docstatus: 1,
+					disabled: 0
+				}
+			}
+		});
+	},
+	employee: function(frm) {
+		if(frm.doc.employee){
+			frappe.call({
+				method: "frappe.client.get_value",
+				args:{
+					doctype: "Employee",
+					fieldname: "company",
+					filters:{
+						name: frm.doc.employee
+					}
+				},
+				callback: function(data) {
+					if(data.message){
+						frm.set_value("company", data.message.company);
+					}
+				}
+			});
+		}
+		else{
+			frm.set_value("company", null);
+		}
+	}
+});
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
new file mode 100644
index 0000000..4f74a7f
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
@@ -0,0 +1,177 @@
+{
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "HR-SSA-.YY.-.MM.-.#####",
+ "creation": "2018-04-13 16:38:41.769237",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "employee",
+  "employee_name",
+  "department",
+  "company",
+  "column_break_6",
+  "designation",
+  "salary_structure",
+  "from_date",
+  "income_tax_slab",
+  "section_break_7",
+  "base",
+  "column_break_9",
+  "variable",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "employee",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Employee",
+   "options": "Employee",
+   "reqd": 1,
+   "search_index": 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",
+   "in_standard_filter": 1,
+   "label": "Department",
+   "options": "Department",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "employee.designation",
+   "fieldname": "designation",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Designation",
+   "options": "Designation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_6",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "salary_structure",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Salary Structure",
+   "options": "Salary Structure",
+   "reqd": 1,
+   "search_index": 1
+  },
+  {
+   "fieldname": "from_date",
+   "fieldtype": "Date",
+   "label": "From Date",
+   "reqd": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_7",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "base",
+   "fieldtype": "Currency",
+   "label": "Base",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "variable",
+   "fieldtype": "Currency",
+   "label": "Variable",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Salary Structure Assignment",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "income_tax_slab",
+   "fieldtype": "Link",
+   "label": "Income Tax Slab",
+   "options": "Income Tax Slab"
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-05-27 19:58:09.964692",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Structure Assignment",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "HR User",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
new file mode 100644
index 0000000..668e0ec
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import getdate
+from frappe.model.document import Document
+
+class DuplicateAssignment(frappe.ValidationError): pass
+
+class SalaryStructureAssignment(Document):
+	def validate(self):
+		self.validate_dates()
+
+	def validate_dates(self):
+		joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
+			["date_of_joining", "relieving_date"])
+
+		if self.from_date:
+			if frappe.db.exists("Salary Structure Assignment", {"employee": self.employee, "from_date": self.from_date, "docstatus": 1}):
+				frappe.throw(_("Salary Structure Assignment for Employee already exists"), DuplicateAssignment)
+
+			if joining_date and getdate(self.from_date) < joining_date:
+				frappe.throw(_("From Date {0} cannot be before employee's joining Date {1}")
+					.format(self.from_date, joining_date))
+
+			# flag - old_employee is for migrating the old employees data via patch
+			if relieving_date and getdate(self.from_date) > relieving_date and not self.flags.old_employee:
+				frappe.throw(_("From Date {0} cannot be after employee's relieving Date {1}")
+					.format(self.from_date, relieving_date))
+
+def get_assigned_salary_structure(employee, on_date):
+	if not employee or not on_date:
+		return None
+	salary_structure = frappe.db.sql("""
+		select salary_structure from `tabSalary Structure Assignment`
+		where employee=%(employee)s
+		and docstatus = 1
+		and %(on_date)s >= from_date order by from_date desc limit 1""", {
+			'employee': employee,
+			'on_date': on_date,
+		})
+	return salary_structure[0][0] if salary_structure else None
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
new file mode 100644
index 0000000..2f52576
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Salary Structure Assignment", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially([
+		// insert a new Salary Structure Assignment
+		() => frappe.tests.make('Salary Structure Assignment', [
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py
new file mode 100644
index 0000000..a9833bf
--- /dev/null
+++ b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestSalaryStructureAssignment(unittest.TestCase):
+	pass
diff --git a/erpnext/payroll/doctype/taxable_salary_slab/__init__.py b/erpnext/payroll/doctype/taxable_salary_slab/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/doctype/taxable_salary_slab/__init__.py
diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json
new file mode 100644
index 0000000..277576e
--- /dev/null
+++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json
@@ -0,0 +1,64 @@
+{
+ "actions": [],
+ "creation": "2018-04-13 17:42:13.516032",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "from_amount",
+  "to_amount",
+  "percent_deduction",
+  "condition",
+  "column_break_5",
+  "html_6"
+ ],
+ "fields": [
+  {
+   "fieldname": "from_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "From Amount",
+   "reqd": 1
+  },
+  {
+   "fieldname": "to_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "To Amount"
+  },
+  {
+   "fieldname": "percent_deduction",
+   "fieldtype": "Percent",
+   "in_list_view": 1,
+   "label": "Percent Deduction",
+   "reqd": 1
+  },
+  {
+   "fieldname": "condition",
+   "fieldtype": "Code",
+   "in_list_view": 1,
+   "label": "Condition"
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "html_6",
+   "fieldtype": "HTML",
+   "options": "<h4>Condition Examples</h4>\n<ol>\n<li>Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)<br>\n<code>Condition: date_of_birth&gt;date(1937, 12, 31) and date_of_birth&lt;date(1958, 01, 01)</code></li><br><li>Applying tax by employee gender<br>\n<code>Condition: gender==\"Male\"</code></li><br>\n<li>Applying tax by Salary Component<br>\n<code>Condition: base &gt; 10000</code></li></ol>"
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-27 23:32:47.253106",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Taxable Salary Slab",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
new file mode 100644
index 0000000..49c5255
--- /dev/null
+++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class TaxableSalarySlab(Document):
+	pass
diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json
new file mode 100644
index 0000000..a4ea536
--- /dev/null
+++ b/erpnext/payroll/module_onboarding/payroll/payroll.json
@@ -0,0 +1,51 @@
+{
+ "allow_roles": [
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "HR User"
+  }
+ ],
+ "creation": "2020-06-01 12:10:52.560472",
+ "docstatus": 0,
+ "doctype": "Module Onboarding",
+ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry",
+ "idx": 0,
+ "is_complete": 0,
+ "modified": "2020-06-04 16:35:30.650792",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Payroll",
+ "owner": "Administrator",
+ "steps": [
+  {
+   "step": "Create Employee"
+  },
+  {
+   "step": "Create Salary Component"
+  },
+  {
+   "step": "Create Payroll Period"
+  },
+  {
+   "step": "Create Income Tax Slab"
+  },
+  {
+   "step": "Create Salary Structure"
+  },
+  {
+   "step": "Assign Salary Structure"
+  },
+  {
+   "step": "Create Salary Slip"
+  },
+  {
+   "step": "Payroll Settings"
+  }
+ ],
+ "subtitle": "Salary, Compensations and more.",
+ "success_message": "The Payroll is all set up!",
+ "title": "Let's Setup the Payroll Module. ",
+ "user_can_dismiss": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as
new file mode 100644
index 0000000..7a39557
--- /dev/null
+++ b/erpnext/payroll/notification/as
@@ -0,0 +1 @@
+update from `tabNotification` set module='Payroll' where name = "Retention Bonus"
\ No newline at end of file
diff --git a/erpnext/payroll/notification/retention_bonus/__init__.py b/erpnext/payroll/notification/retention_bonus/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/notification/retention_bonus/__init__.py
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
new file mode 100644
index 0000000..50db033
--- /dev/null
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json
@@ -0,0 +1,26 @@
+{
+ "attach_print": 0,
+ "condition": "doc.docstatus==1",
+ "creation": "2018-05-15 18:52:36.362838",
+ "date_changed": "bonus_payment_date",
+ "days_in_advance": 14,
+ "docstatus": 0,
+ "doctype": "Notification",
+ "document_type": "Retention Bonus",
+ "enabled": 1,
+ "event": "Days Before",
+ "idx": 0,
+ "is_standard": 1,
+ "message": "<p>{{ _(\"Hello\") }},</p>\n\n<p> {{ _(\"Retention Bonus for\") }} {{ doc.employee_name }} {{ _(\"due on\") }} {{ doc.bonus_payment_date }}</p>",
+ "modified": "2018-05-15 19:00:24.294418",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Retention Bonus",
+ "owner": "Administrator",
+ "recipients": [
+  {
+   "email_by_role": "HR Manager"
+  }
+ ],
+ "subject": "Retention Bonus alert for {{ doc.employee }}"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.md b/erpnext/payroll/notification/retention_bonus/retention_bonus.md
new file mode 100644
index 0000000..8f48193
--- /dev/null
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.md
@@ -0,0 +1,3 @@
+<p>{{ _("Hello") }},</p>
+
+<p> {{ _("Retention Bonus for") }} {{ doc.employee_name }} {{ _("due on") }} {{ doc.bonus_payment_date }}</p>
\ No newline at end of file
diff --git a/erpnext/payroll/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
new file mode 100644
index 0000000..2334f8b
--- /dev/null
+++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+
+import frappe
+
+def get_context(context):
+	# do your magic here
+	pass
diff --git a/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json
new file mode 100644
index 0000000..8a07b10
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:58:43.927590",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 1,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:58:43.927590",
+ "modified_by": "Administrator",
+ "name": "Assign Salary Structure",
+ "owner": "Administrator",
+ "reference_document": "Salary Structure Assignment",
+ "show_full_form": 1,
+ "title": "Assign Salary Structure",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_employee/create_employee.json b/erpnext/payroll/onboarding_step/create_employee/create_employee.json
new file mode 100644
index 0000000..5839ae6
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_employee/create_employee.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-05-14 11:43:25.561152",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 1,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-05-14 12:26:28.629074",
+ "modified_by": "Administrator",
+ "name": "Create Employee",
+ "owner": "Administrator",
+ "reference_document": "Employee",
+ "show_full_form": 0,
+ "title": "Create Employee",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json
new file mode 100644
index 0000000..faada7e
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:54:54.823796",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:54:54.823796",
+ "modified_by": "Administrator",
+ "name": "Create Income Tax Slab",
+ "owner": "Administrator",
+ "reference_document": "Income Tax Slab",
+ "show_full_form": 1,
+ "title": "Create Income Tax Slab",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json
new file mode 100644
index 0000000..4bae675
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:53:54.553947",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 1,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:53:54.553947",
+ "modified_by": "Administrator",
+ "name": "Create Payroll Period",
+ "owner": "Administrator",
+ "reference_document": "Payroll Period",
+ "show_full_form": 0,
+ "title": "Create Payroll Period",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json
new file mode 100644
index 0000000..002d819
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:57:04.002073",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:57:04.002073",
+ "modified_by": "Administrator",
+ "name": "Create Salary Component",
+ "owner": "Administrator",
+ "reference_document": "Salary Component",
+ "show_full_form": 1,
+ "title": "Create Salary Component",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json
new file mode 100644
index 0000000..2aa31f4
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:59:29.972393",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 1,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:59:29.972393",
+ "modified_by": "Administrator",
+ "name": "Create Salary Slip",
+ "owner": "Administrator",
+ "reference_document": "Salary Slip",
+ "show_full_form": 1,
+ "title": "Create Salary Slip",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json
new file mode 100644
index 0000000..11d8327
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json
@@ -0,0 +1,19 @@
+{
+ "action": "Create Entry",
+ "creation": "2020-06-01 11:57:54.527808",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 1,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-01 11:57:54.527808",
+ "modified_by": "Administrator",
+ "name": "Create Salary Structure",
+ "owner": "Administrator",
+ "reference_document": "Salary Structure",
+ "show_full_form": 1,
+ "title": "Create Salary Structure",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json
new file mode 100644
index 0000000..946b8c8
--- /dev/null
+++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json
@@ -0,0 +1,19 @@
+{
+ "action": "Go to Page",
+ "creation": "2020-06-04 16:34:29.664917",
+ "docstatus": 0,
+ "doctype": "Onboarding Step",
+ "idx": 0,
+ "is_complete": 0,
+ "is_mandatory": 0,
+ "is_single": 0,
+ "is_skipped": 0,
+ "modified": "2020-06-04 16:34:29.664917",
+ "modified_by": "Administrator",
+ "name": "Payroll Settings",
+ "owner": "Administrator",
+ "path": "#Form/Payroll Settings",
+ "show_full_form": 0,
+ "title": "Payroll Settings",
+ "validate_action": 1
+}
\ No newline at end of file
diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py
diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json
new file mode 100644
index 0000000..ceaf4a6
--- /dev/null
+++ b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json
@@ -0,0 +1,18 @@
+{
+ "creation": "2016-07-07 12:38:32.447281",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Salary Slip",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \"  <h3 style=\\\"text-align: right;\\\">{{doc.name}}</h3><div><hr></div>  \"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\"}, {\"print_hide\": 0, \"fieldname\": \"department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_hours\"}, {\"print_hide\": 0, \"fieldname\": \"hour_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"time_sheet\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"working_hours\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"timesheets\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\"}]",
+ "idx": 0,
+ "modified": "2016-08-21 21:02:59.896033",
+ "modified_by": "Administrator",
+ "name": "Salary Slip based on Timesheet",
+ "owner": "Administrator",
+ "print_format_builder": 1,
+ "print_format_type": "Server",
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/print_format/salary_slip_standard/__init__.py b/erpnext/payroll/print_format/salary_slip_standard/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/print_format/salary_slip_standard/__init__.py
diff --git a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json
new file mode 100644
index 0000000..b01239f
--- /dev/null
+++ b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json
@@ -0,0 +1,22 @@
+{
+ "align_labels_right": 0,
+ "creation": "2016-07-07 11:45:14.872204",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Salary Slip",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"   <h3 style=\\\"text-align: right;\\\"><span style=\\\"line-height: 1.42857;\\\">{{doc.name}}</span></h3>\\n<div>\\n    <hr style=\\\"text-align: center;\\\">\\n</div>   \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\", \"label\": \"Employee\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\", \"label\": \"Employee Name\"}, {\"print_hide\": 0, \"fieldname\": \"department\", \"label\": \"Department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\", \"label\": \"Designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\", \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\", \"label\": \"Start Date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\", \"label\": \"End Date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_days\", \"label\": \"Working Days\"}, {\"print_hide\": 0, \"fieldname\": \"leave_without_pay\", \"label\": \"Leave Without Pay\"}, {\"print_hide\": 0, \"fieldname\": \"payment_days\", \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\", \"label\": \"Earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\", \"label\": \"Deductions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\", \"label\": \"Gross Pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\", \"label\": \"Total Deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\", \"label\": \"Net Pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\", \"label\": \"Total in words\"}]",
+ "idx": 0,
+ "line_breaks": 0,
+ "modified": "2018-07-24 19:31:39.040701",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Salary Slip Standard",
+ "owner": "Administrator",
+ "print_format_builder": 1,
+ "print_format_type": "Server",
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/__init__.py b/erpnext/payroll/report/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/__init__.py
diff --git a/erpnext/payroll/report/bank_remittance/__init__.py b/erpnext/payroll/report/bank_remittance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/bank_remittance/__init__.py
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js
new file mode 100644
index 0000000..6482ed3
--- /dev/null
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Bank Remittance"] = {
+	"filters": [
+		{
+			fieldname:"company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+			reqd: 1
+		},
+		{
+			fieldname:"from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+		},
+		{
+			fieldname:"to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+		},
+
+	]
+}
+
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.json b/erpnext/payroll/report/bank_remittance/bank_remittance.json
new file mode 100644
index 0000000..2a697b2
--- /dev/null
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.json
@@ -0,0 +1,24 @@
+{
+ "add_total_row": 0,
+ "creation": "2019-03-26 16:57:52.558895",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-05-28 00:08:08.097494",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Bank Remittance",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Payroll Entry",
+ "report_name": "Bank Remittance",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "HR Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py
new file mode 100644
index 0000000..a35d8e5
--- /dev/null
+++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py
@@ -0,0 +1,157 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import formatdate
+import itertools
+from frappe import _, get_all
+
+def execute(filters=None):
+	columns = [
+		{
+			"label": _("Payroll Number"),
+			"fieldtype": "Link",
+			"fieldname": "payroll_no",
+			"options": "Payroll Entry",
+			"width": 150
+		},
+		{
+			"label": _("Debit A/C Number"),
+			"fieldtype": "Int",
+			"fieldname": "debit_account",
+			"hidden": 1,
+			"width": 200
+		},
+		{
+			"label": _("Payment Date"),
+			"fieldtype": "Data",
+			"fieldname": "payment_date",
+			"width": 100
+		},
+		{
+			"label": _("Employee Name"),
+			"fieldtype": "Link",
+			"fieldname": "employee_name",
+			"options": "Employee",
+			"width": 200
+		},
+		{
+			"label": _("Bank Name"),
+			"fieldtype": "Data",
+			"fieldname": "bank_name",
+			"width": 50
+		},
+		{
+			"label": _("Employee A/C Number"),
+			"fieldtype": "Int",
+			"fieldname": "employee_account_no",
+			"width": 50
+		},
+		{
+			"label": _("IFSC Code"),
+			"fieldtype": "Data",
+			"fieldname": "bank_code",
+			"width": 100
+		},
+		{
+			"label": _("Currency"),
+			"fieldtype": "Data",
+			"fieldname": "currency",
+			"width": 50
+		},
+		{
+			"label": _("Net Salary Amount"),
+			"fieldtype": "Currency",
+			"options": "currency",
+			"fieldname": "amount",
+			"width": 100
+		}
+	]
+	data = []
+
+	accounts = get_bank_accounts()
+	payroll_entries = get_payroll_entries(accounts, filters)
+	salary_slips = get_salary_slips(payroll_entries)
+	get_emp_bank_ifsc_code(salary_slips)
+
+	for salary in salary_slips:
+		if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
+			row = {
+				"payroll_no": salary.payroll_entry,
+				"debit_account": salary.debit_acc_no,
+				"payment_date": frappe.utils.formatdate(salary.modified.strftime('%Y-%m-%d')),
+				"bank_name": salary.bank_name,
+				"employee_account_no": salary.bank_account_no,
+				"bank_code": salary.ifsc_code,
+				"employee_name": salary.employee+": " + salary.employee_name,
+				"currency": frappe.get_cached_value('Company', filters.company, 'default_currency'),
+				"amount": salary.net_pay,
+			}
+			data.append(row)
+	return columns, data
+
+def get_bank_accounts():
+	accounts = [d.name for d in get_all("Account", filters={"account_type": "Bank"})]
+	return accounts
+
+def get_payroll_entries(accounts, filters):
+	payroll_filter = [
+		('payment_account', 'IN', accounts),
+		('number_of_employees', '>', 0),
+		('Company', '=', filters.company)
+	]
+	if filters.to_date:
+		payroll_filter.append(('posting_date', '<', filters.to_date))
+
+	if filters.from_date:
+		payroll_filter.append(('posting_date', '>', filters.from_date))
+
+	entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"])
+
+	payment_accounts = [d.payment_account for d in entries]
+	set_company_account(payment_accounts, entries)
+	return entries
+
+def get_salary_slips(payroll_entries):
+	payroll  = [d.name for d in payroll_entries]
+	salary_slips = get_all("Salary Slip", filters = [("payroll_entry", "IN", payroll)],
+		fields = ["modified", "net_pay", "bank_name", "bank_account_no", "payroll_entry", "employee", "employee_name", "status"]
+	)
+
+	payroll_entry_map = {}
+	for entry in payroll_entries:
+		payroll_entry_map[entry.name] = entry
+
+	# appending company debit accounts
+	for slip in salary_slips:
+		if slip.payroll_entry:
+			slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account']
+		else:
+			slip["debit_acc_no"] = None
+
+	return salary_slips
+
+def get_emp_bank_ifsc_code(salary_slips):
+	emp_names = [d.employee for d in salary_slips]
+	ifsc_codes = get_all("Employee", [("name", "IN", emp_names)], ["ifsc_code", "name"])
+
+	ifsc_codes_map = {}
+	for code in ifsc_codes:
+		ifsc_codes_map[code.name] = code
+
+	for slip in salary_slips:
+		slip["ifsc_code"] = ifsc_codes_map[code.name]['ifsc_code']
+
+	return salary_slips
+
+def set_company_account(payment_accounts, payroll_entries):
+	company_accounts = get_all("Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"])
+	company_accounts_map = {}
+	for acc in company_accounts:
+		company_accounts_map[acc.account] = acc
+
+	for entry in payroll_entries:
+		entry["company_account"] = company_accounts_map[entry.payment_account]['bank_account_no']
+
+	return payroll_entries
diff --git a/erpnext/payroll/report/income_tax_deductions/__init__.py b/erpnext/payroll/report/income_tax_deductions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_deductions/__init__.py
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js
new file mode 100644
index 0000000..4bbb7f6
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
+	frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters;
+});
\ No newline at end of file
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json
new file mode 100644
index 0000000..cf80398
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json
@@ -0,0 +1,30 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-05-30 00:07:56.744372",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-05-30 00:07:56.744372",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Income Tax Deductions",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Income Tax Deductions",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "HR User"
+  },
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "Employee"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
new file mode 100644
index 0000000..3bad587
--- /dev/null
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe import _
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+
+	return columns, data
+
+def get_columns(filters):
+	columns = [
+		{
+			"label": _("Employee"),
+			"options": "Employee",
+			"fieldname": "employee",
+			"fieldtype": "Link",
+			"width": 200
+		},
+		{
+			"label": _("Employee Name"),
+			"options": "Employee",
+			"fieldname": "employee_name",
+			"fieldtype": "Link",
+			"width": 160
+		}]
+
+	if erpnext.get_region() == "India":
+		columns.append({
+			"label": _("PAN Number"),
+			"fieldname": "pan_number",
+			"fieldtype": "Data",
+			"width": 140
+		})
+
+	columns += [{
+			"label": _("Income Tax Component"),
+			"fieldname": "it_comp",
+			"fieldtype": "Data",
+			"width": 170
+		},
+		{
+			"label": _("Income Tax Amount"),
+			"fieldname": "it_amount",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 140
+		},
+		{
+			"label": _("Gross Pay"),
+			"fieldname": "gross_pay",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 140
+		},
+		{
+			"label": _("Posting Date"),
+			"fieldname": "posting_date",
+			"fieldtype": "Date",
+			"width": 140
+		}
+	]
+
+	return columns
+
+def get_conditions(filters):
+	conditions = [""]
+
+	if filters.get("department"):
+		conditions.append("sal.department = '%s' " % (filters["department"]) )
+
+	if filters.get("branch"):
+		conditions.append("sal.branch = '%s' " % (filters["branch"]) )
+
+	if filters.get("company"):
+		conditions.append("sal.company = '%s' " % (filters["company"]) )
+
+	if filters.get("period"):
+		conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
+
+	return " and ".join(conditions)
+
+
+def get_data(filters):
+
+	data = []
+
+	if erpnext.get_region() == "India":
+		employee_pan_dict = frappe._dict(frappe.db.sql(""" select employee, pan_number from `tabEmployee`"""))
+
+	component_types = frappe.db.sql(""" select name from `tabSalary Component`
+		where is_income_tax_component = 1 """)
+
+	component_types = [comp_type[0] for comp_type in component_types]
+
+	conditions = get_conditions(filters)
+
+	entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay
+		from `tabSalary Slip` sal, `tabSalary Detail` ded
+		where sal.name = ded.parent
+		and ded.parentfield = 'deductions'
+		and ded.parenttype = 'Salary Slip'
+		and sal.docstatus = 1 %s
+		and ded.salary_component in (%s)
+	""" % (conditions , ", ".join(['%s']*len(component_types))), tuple(component_types), as_dict=1)
+
+	for d in entry:
+
+		employee = {
+			"employee": d.employee,
+			"employee_name": d.employee_name,
+			"it_comp": d.salary_component,
+			"posting_date": d.posting_date,
+			# "pan_number": employee_pan_dict.get(d.employee),
+			"it_amount": d.amount,
+			"gross_pay": d.gross_pay
+		}
+
+		if erpnext.get_region() == "India":
+			employee["pan_number"] = employee_pan_dict.get(d.employee)
+
+		data.append(employee)
+
+	return data
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js
new file mode 100644
index 0000000..166d982
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
+	frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters;
+});
\ No newline at end of file
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json
new file mode 100644
index 0000000..c04cc32
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json
@@ -0,0 +1,30 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-06-16 18:43:43.107246",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-16 18:43:43.107246",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Payments Based On Payment Mode",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Salary Payments Based On Payment Mode",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "HR User"
+  },
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "Employee"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
new file mode 100644
index 0000000..7f0c2e2
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe import _
+from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
+
+def execute(filters=None):
+	mode_of_payments = get_payment_modes()
+
+	if not len(mode_of_payments):
+		return [], []
+
+	columns = get_columns(filters, mode_of_payments)
+	data, total_rows, report_summary = get_data(filters, mode_of_payments)
+	chart =  get_chart(mode_of_payments, total_rows)
+
+	return columns, data, None, chart, report_summary
+
+def get_columns(filters, mode_of_payments):
+	columns = [{
+		"label": _("Branch"),
+		"options": "Branch",
+		"fieldname": "branch",
+		"fieldtype": "Link",
+		"width": 200
+	}]
+
+	for mode in mode_of_payments:
+		columns.append({
+			"label": _(mode),
+			"fieldname": mode,
+			"fieldtype": "Currency",
+			"width": 160
+		})
+
+	columns.append({
+		"label": _("Total"),
+		"fieldname": "total",
+		"fieldtype": "Currency",
+		"width": 140
+	})
+
+	return columns
+
+def get_payment_modes():
+	mode_of_payments = frappe.db.sql_list("""
+		select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1
+	""")
+	return mode_of_payments
+
+def prepare_data(entry):
+	branch_wise_entries = {}
+	gross_pay = 0
+
+	for d in entry:
+		gross_pay += d.gross_pay
+		if branch_wise_entries.get(d.branch):
+			branch_wise_entries[d.branch][d.mode_of_payment] = d.net_pay
+		else:
+			branch_wise_entries.setdefault(d.branch, {}).setdefault(d.mode_of_payment, d.net_pay)
+
+	return branch_wise_entries, gross_pay
+
+def get_data(filters, mode_of_payments):
+	data = []
+
+	conditions = get_conditions(filters)
+
+	entry = frappe.db.sql("""
+		select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay
+		from `tabSalary Slip` sal
+		where docstatus = 1 %s
+		group by branch, mode_of_payment
+		""" % (conditions), as_dict=1)
+
+	branch_wise_entries, gross_pay = prepare_data(entry)
+
+	branches = frappe.db.sql_list("""
+		select distinct branch from `tabSalary Slip` sal
+		where docstatus = 1 %s
+	""" % (conditions))
+
+	total_row = {"total": 0, "branch": "Total"}
+
+	for branch in branches:
+		total = 0
+		row = {
+			"branch": branch
+		}
+		for mode in mode_of_payments:
+			if branch_wise_entries.get(branch).get(mode):
+				row[mode] = branch_wise_entries.get(branch).get(mode)
+				total +=  branch_wise_entries.get(branch).get(mode)
+
+		row["total"] = total
+		data.append(row)
+
+	total_row = get_total_based_on_mode_of_payment(data, mode_of_payments)
+	total_deductions = gross_pay - total_row.get("total")
+
+	if data:
+		data.append(total_row)
+		data.append({})
+		data.append({
+			"branch": "<b>Total Gross Pay</b>",
+			mode_of_payments[0]:gross_pay
+		})
+		data.append({
+			"branch": "<b>Total Deductions</b>",
+			mode_of_payments[0]:total_deductions
+		})
+		data.append({
+			"branch": "<b>Total Net Pay</b>",
+			mode_of_payments[0]:total_row.get("total")
+		})
+
+		currency = erpnext.get_company_currency(filters.company)
+		report_summary = get_report_summary(gross_pay, total_deductions, total_row.get("total"), currency)
+
+	return data, total_row, report_summary
+
+def get_total_based_on_mode_of_payment(data, mode_of_payments):
+
+	total = 0
+	total_row = {"branch": "<b>Total</b>"}
+	for mode in mode_of_payments:
+		sum_of_payment = sum([detail[mode] for detail in data if mode in detail.keys()])
+		total_row[mode] = sum_of_payment
+		total += sum_of_payment
+
+	total_row["total"] = total
+	return total_row
+
+def get_report_summary(gross_pay, total_deductions, net_pay, currency):
+	return [
+		{
+			"value": gross_pay,
+			"label": "Total Gross Pay",
+			"indicator": "Green",
+			"datatype": "Currency",
+			"currency": currency
+		},
+		{
+			"value": total_deductions,
+			"label": "Total Deduction",
+			"datatype": "Currency",
+			"indicator": "Red",
+			"currency": currency
+		},
+		{
+			"value": net_pay,
+			"label": "Total Net Pay",
+			"datatype": "Currency",
+			"indicator": "Blue",
+			"currency": currency
+		}
+	]
+
+def get_chart(mode_of_payments, data):
+	if data:
+		values = []
+		labels = []
+
+		for mode in mode_of_payments:
+			values.append(data[mode])
+			labels.append([mode])
+
+		chart = {
+			"data": {
+				"labels": labels,
+				"datasets": [{'name': 'Mode Of Payments', "values": values}]
+			}
+		}
+		chart['type'] = "bar"
+		return chart
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/__init__.py b/erpnext/payroll/report/salary_payments_via_ecs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_via_ecs/__init__.py
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js
new file mode 100644
index 0000000..e49fc11
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() {
+
+	let ecs_checklist_filter = erpnext.salary_slip_deductions_report_filters
+	ecs_checklist_filter['filters'].push({
+		fieldname: "type",
+		label: __("Type"),
+		fieldtype: "Select",
+		options:["", "Bank", "Cash", "Cheque"]
+	})
+
+	frappe.query_reports["Salary Payments via ECS"] = ecs_checklist_filter
+});
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json
new file mode 100644
index 0000000..dd0ac7c
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 0,
+ "creation": "2020-06-16 18:35:30.508143",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-16 18:38:23.680185",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Payments via ECS",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Salary Payments via ECS",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "HR Manager"
+  },
+  {
+   "role": "HR User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
new file mode 100644
index 0000000..afd5c13
--- /dev/null
+++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
@@ -0,0 +1,146 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, erpnext
+from frappe import _
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+
+	return columns, data
+
+def get_columns(filters):
+	columns = [
+		{
+			"label": _("Branch"),
+			"options": "Branch",
+			"fieldname": "branch",
+			"fieldtype": "Link",
+			"width": 200
+		},
+		{
+			"label": _("Employee Name"),
+			"options": "Employee",
+			"fieldname": "employee_name",
+			"fieldtype": "Link",
+			"width": 160
+		},
+		{
+			"label": _("Employee"),
+			"options":"Employee",
+			"fieldname": "employee",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Gross Pay"),
+			"fieldname": "gross_pay",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 140
+		},
+		{
+			"label": _("Bank"),
+			"fieldname": "bank",
+			"fieldtype": "Data",
+			"width": 140
+		},
+		{
+			"label": _("Account No"),
+			"fieldname": "account_no",
+			"fieldtype": "Data",
+			"width": 140
+		},
+	]
+	if erpnext.get_region() == "India":
+		columns += [
+			{
+				"label": _("IFSC"),
+				"fieldname": "ifsc",
+				"fieldtype": "Data",
+				"width": 140
+			},
+			{
+				"label": _("MICR"),
+				"fieldname": "micr",
+				"fieldtype": "Data",
+				"width": 140
+			}
+		]
+
+	return columns
+
+def get_conditions(filters):
+	conditions = [""]
+
+	if filters.get("department"):
+		conditions.append("department = '%s' " % (filters["department"]) )
+
+	if filters.get("branch"):
+		conditions.append("branch = '%s' " % (filters["branch"]) )
+
+	if filters.get("company"):
+		conditions.append("company = '%s' " % (filters["company"]) )
+
+	if filters.get("period"):
+		conditions.append("month(start_date) = '%s' " % (filters["period"]))
+
+	return " and ".join(conditions)
+
+def get_data(filters):
+
+	data = []
+
+	fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"]
+	if erpnext.get_region() == "India":
+		fields += ["ifsc_code", "micr_code"]
+
+
+	employee_details = frappe.get_list("Employee", fields = fields)
+	employee_data_dict = {}
+
+	for d in employee_details:
+		employee_data_dict.setdefault(
+			d.employee,{
+				"bank_ac_no" : d.bank_ac_no,
+				"ifsc_code" : d.ifsc_code or None,
+				"micr_code" : d.micr_code or  None,
+				"branch" : d.branch,
+				"salary_mode" : d.salary_mode,
+				"bank_name": d.bank_name
+			}
+		)
+
+	conditions = get_conditions(filters)
+
+	entry = frappe.db.sql(""" select employee, employee_name, gross_pay
+		from `tabSalary Slip`
+		where docstatus = 1 %s """
+		%(conditions), as_dict =1)
+
+	for d in entry:
+
+		employee = {
+			"branch" : employee_data_dict.get(d.employee).get("branch"),
+			"employee_name" : d.employee_name,
+			"employee" : d.employee,
+			"gross_pay" : d.gross_pay,
+		}
+
+		if employee_data_dict.get(d.employee).get("salary_mode") == "Bank":
+			employee["bank"] = employee_data_dict.get(d.employee).get("bank_name")
+			employee["account_no"] = employee_data_dict.get(d.employee).get("bank_ac_no")
+			if erpnext.get_region() == "India":
+				employee["ifsc"] = employee_data_dict.get(d.employee).get("ifsc_code")
+				employee["micr"] = employee_data_dict.get(d.employee).get("micr_code")
+		else:
+			employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode")
+
+		if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get("type"):
+			data.append(employee)
+		elif not filters.get("type"):
+			data.append(employee)
+
+	return data
diff --git a/erpnext/payroll/report/salary_register/__init__.py b/erpnext/payroll/report/salary_register/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/payroll/report/salary_register/__init__.py
diff --git a/erpnext/payroll/report/salary_register/salary_register.html b/erpnext/payroll/report/salary_register/salary_register.html
new file mode 100644
index 0000000..3abc3a0
--- /dev/null
+++ b/erpnext/payroll/report/salary_register/salary_register.html
@@ -0,0 +1,40 @@
+{%
+	var report_columns = report.get_columns_for_print();
+%}
+<div style="margin-bottom: 7px;" class="text-center">
+	{%= frappe.boot.letter_heads[filters.letter_head || frappe.defaults.get_default("letter_head")].header %}
+</div>
+<h2 class="text-center">{%= __(report.report_name) %}</h2>
+<h5 class="text-center">{{ __("From") }} {%= filters.from_date %} {{ __("to") }} {%= filters.to_date %}</h5>
+<hr>
+<table class="table table-bordered">
+	<thead>
+		<tr>
+			{% for(var i=1, l=report_columns.length; i<l; i++) { %}
+				<th class="text-right">{%= report_columns[i].label %}</th>
+			{% } %}
+		</tr>
+	</thead>
+	<tbody>
+		{% for(var j=0, k=data.length; j<k; j++) { %}
+			{%
+				var row = data[j];
+			%}
+			<tr>
+				{% for(var i=1, l=report_columns.length; i<l; i++) { %}
+					<td class="text-right">
+						{% var fieldname = report_columns[i].fieldname; %}
+						{% if (report_columns[i].fieldtype=='Currency' && !isNaN(row[fieldname])) { %}
+							{%= format_currency(row[fieldname]) %}
+						{% } else { %}
+							{% if (!is_null(row[fieldname])) { %}
+								{%= row[fieldname] %}
+							{% } %}
+						{% } %}
+					</td>
+				{% } %}
+			</tr>
+		{% } %}
+	</tbody>
+</table>
+<p class="text-right text-muted">{{ __("Printed On") }} {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
diff --git a/erpnext/payroll/report/salary_register/salary_register.js b/erpnext/payroll/report/salary_register/salary_register.js
new file mode 100644
index 0000000..885e3d1
--- /dev/null
+++ b/erpnext/payroll/report/salary_register/salary_register.js
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.query_reports["Salary Register"] = {
+	"filters": [
+		{
+			"fieldname":"from_date",
+			"label": __("From"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(),-1),
+			"reqd": 1
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("To"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.get_today(),
+			"reqd": 1
+		},
+		{
+			"fieldname":"employee",
+			"label": __("Employee"),
+			"fieldtype": "Link",
+			"options": "Employee"
+		},
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company")
+		},
+		{
+			"fieldname":"docstatus",
+			"label":__("Document Status"),
+			"fieldtype":"Select",
+			"options":["Draft", "Submitted", "Cancelled"],
+			"default":"Submitted"
+		}
+	]
+}
diff --git a/erpnext/payroll/report/salary_register/salary_register.json b/erpnext/payroll/report/salary_register/salary_register.json
new file mode 100644
index 0000000..5a70c32
--- /dev/null
+++ b/erpnext/payroll/report/salary_register/salary_register.json
@@ -0,0 +1,27 @@
+{
+ "add_total_row": 1,
+ "creation": "2017-01-10 17:36:58.153863",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 2,
+ "is_standard": "Yes",
+ "modified": "2020-05-28 00:07:18.576661",
+ "modified_by": "Administrator",
+ "module": "Payroll",
+ "name": "Salary Register",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Salary Slip",
+ "report_name": "Salary Register",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "HR User"
+  },
+  {
+   "role": "HR Manager"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py
new file mode 100644
index 0000000..8701085
--- /dev/null
+++ b/erpnext/payroll/report/salary_register/salary_register.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import flt
+from frappe import _
+
+def execute(filters=None):
+	if not filters: filters = {}
+	salary_slips = get_salary_slips(filters)
+	if not salary_slips: return [], []
+
+	columns, earning_types, ded_types = get_columns(salary_slips)
+	ss_earning_map = get_ss_earning_map(salary_slips)
+	ss_ded_map = get_ss_ded_map(salary_slips)
+	doj_map = get_employee_doj_map()
+
+	data = []
+	for ss in salary_slips:
+		row = [ss.name, ss.employee, ss.employee_name, doj_map.get(ss.employee), ss.branch, ss.department, ss.designation,
+			ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days]
+
+		if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
+		if not ss.department  == None: columns[4] = columns[4].replace('-1','120')
+		if not ss.designation  == None: columns[5] = columns[5].replace('-1','120')
+		if not ss.leave_without_pay  == None: columns[9] = columns[9].replace('-1','130')
+
+
+		for e in earning_types:
+			row.append(ss_earning_map.get(ss.name, {}).get(e))
+
+		row += [ss.gross_pay]
+
+		for d in ded_types:
+			row.append(ss_ded_map.get(ss.name, {}).get(d))
+
+		row.append(ss.total_loan_repayment)
+
+		row += [ss.total_deduction, ss.net_pay]
+
+		data.append(row)
+
+	return columns, data
+
+def get_columns(salary_slips):
+	"""
+	columns = [
+		_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
+		_("Date of Joining") + "::80", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
+		_("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
+		_("End Date") + "::80", _("Leave Without Pay") + ":Float:130", _("Payment Days") + ":Float:120"
+	]
+	"""
+	columns = [
+		_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
+		_("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1",
+		_("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
+		_("End Date") + "::80", _("Leave Without Pay") + ":Float:50", _("Payment Days") + ":Float:120"
+	]
+
+	salary_components = {_("Earning"): [], _("Deduction"): []}
+
+	for component in frappe.db.sql("""select distinct sd.salary_component, sc.type
+		from `tabSalary Detail` sd, `tabSalary Component` sc
+		where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)""" %
+		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1):
+		salary_components[_(component.type)].append(component.salary_component)
+
+	columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
+		[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
+		[_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
+
+	return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
+
+def get_salary_slips(filters):
+	filters.update({"from_date": filters.get("from_date"), "to_date":filters.get("to_date")})
+	conditions, filters = get_conditions(filters)
+	salary_slips = frappe.db.sql("""select * from `tabSalary Slip` where %s
+		order by employee""" % conditions, filters, as_dict=1)
+
+	return salary_slips or []
+
+def get_conditions(filters):
+	conditions = ""
+	doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2}
+
+	if filters.get("docstatus"):
+		conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")])
+
+	if filters.get("from_date"): conditions += " and start_date >= %(from_date)s"
+	if filters.get("to_date"): conditions += " and end_date <= %(to_date)s"
+	if filters.get("company"): conditions += " and company = %(company)s"
+	if filters.get("employee"): conditions += " and employee = %(employee)s"
+
+	return conditions, filters
+
+def get_employee_doj_map():
+	return	frappe._dict(frappe.db.sql("""
+				SELECT
+					employee,
+					date_of_joining
+				FROM `tabEmployee`
+				"""))
+
+def get_ss_earning_map(salary_slips):
+	ss_earnings = frappe.db.sql("""select parent, salary_component, amount
+		from `tabSalary Detail` where parent in (%s)""" %
+		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
+
+	ss_earning_map = {}
+	for d in ss_earnings:
+		ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
+		ss_earning_map[d.parent][d.salary_component] = flt(d.amount)
+
+	return ss_earning_map
+
+def get_ss_ded_map(salary_slips):
+	ss_deductions = frappe.db.sql("""select parent, salary_component, amount
+		from `tabSalary Detail` where parent in (%s)""" %
+		(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
+
+	ss_ded_map = {}
+	for d in ss_deductions:
+		ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
+		ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
+
+	return ss_ded_map